Press enter to see results or esc to cancel.

Custom post type archive pagination showing 404 error

I recently ran into an error while creating an archive page for a WordPress custom post type that had me really stumped. The archive page for the custom post type showed a 404 error when an existing paginated page was meant to be shown in its place.

Say you create a custom post type called ‘house’ (‘houses’ plural). In order for permalinks to work and for posts of this custom post type to be accessible via nicely formatted urls you need to set permalink structure to /%postname%/ and hit save (to flush rewrite rules).

Now, if you publish 7 of these ‘house’ custom posts and you create an archive-house.php file where you run a simple WP_Query to render 5 houses per page, you expect to see 5 house posts on the first page under and 2 houses on the second page under, right?

if ( get_query_var('paged') ) {
    $paged = get_query_var('paged');
} else if ( get_query_var('page') ) {
    $paged = get_query_var('page');
} else {
    $paged = 1;
$posts_per_page = 5;

$args = array(
	'posts_per_page'   => $posts_per_page,
	'paged'		   => $paged,
	'offset'           => 0,
	'category'         => '',
	'orderby'          => 'post_title',
	'order'            => 'DESC',
	'post_type'        => 'house',
	'post_status'      => 'publish'); 

$query = new WP_Query($args); 

Wrong? What you actually see is 5 ‘house’ custom posts on the first page and a 404 error on

I found a number of suggested solutions to this problem, none of which actually address the underlying issue – except one that got me thinking in the right direction.

The problem is actually caused by WordPress core using it’s inbuilt global posts_per_page variable to determine archive pagination even for custom post types. Indeed, as wpessence above suggests you can set posts_per_page to 1 in Settings -> Reading under “Blog pages show at most X posts”, however, that will mean your actual blog posts index will show whatever you set here too.

A more flexible solution is to set the global posts_per_page variable separately for each custom post type.

You can do that by adding something like the following to your functions.php file:

function custom_posts_per_page( $query ) { 
    $houses_archive_posts_per_page = 5;
    $some_other_post_type_posts_per_page = 20;
    if ( isset($query->query_vars['post_type']) && $query->query_vars['post_type'] == 'house' && is_post_type_archive('house') ) 
        $query->query_vars['posts_per_page'] = $houses_archive_posts_per_page ;  
    else if ( isset($query->query_vars['post_type']) && $query->query_vars['post_type'] == 'some_other_custom_post_type' && is_post_type_archive('some_other_custom_post_type') ) 
	$query->query_vars['posts_per_page'] = $some_other_post_type_posts_per_page ;  
    return $query;  
if ( !is_admin() ) 
    add_filter( 'pre_get_posts', 'custom_posts_per_page' ); 

The above solution gives you the flexibility to separately paginate each of your custom post types and still allow your blog posts pagination to be configurable from Settings->Reading.




wow…rezolvet my problem.

‘So I checked the admin panel, and yes, there it was, Under Settings -> Reading: Blog posts show at most 10 posts.
So, I changed that to 1 to facilitate all other custom pagination (with other posts_per_page values), and make sure the empty archive error didn’t occur anymore unless the archives were really empty.’


Great Bucur, glad you found the post useful!


Wow. I’ve spent lots of time working through this error. I’ve probably looked at 100 posts about this particular problem, and you are the first to give a real solution. Who wants a hack solution like changing the Settings > Reading settings to 1? That’s not very forward compatible if one may ever need a regular ‘posts’ blog. Thank you for the $query solution in the ‘pre_get_posts’ hook in functions.php.

Btw, I tried putting the code in my template file instead, but it didn’t work. Have any idea why?

Rafael Slonik

Thank You. :)

Rafael Slonik

Worked for page 2 but don’t for page 3…. :(

Chris Stephens

Hey, thank you so much for this and thank goodness for Google!


Great, glad you guys found this useful.

@Joe, you can’t put the hooks in your template because they happen too late in the execution cycle (if that’s what you were asking).

Leave a Comment