Pagination returns 404 after page 20

What you are experiencing is quite normal and expected. This is one of the big reasons I always hammer on this point, never use custom queries to replace the main query on the home page or any type of archive page. They solve one issue but creates plenty other new ones

Lets look at what you have and why you are getting these results:

BASICS

Although you don’t see the output on your archive page, the main query executes on every page load and returns an array of posts regardless. Removing the loop does not stop the main query from being executed. The loop just displays what is being retrieved by the main query. So what is basically happening, you are retrieving two sets of posts per page load if you replace the default loop with a custom one. The posts retrieved by the main query and posts retrieved by the custom query. If you want to test this, add the following line of code outside the loop on your archive page within php tags

?><pre><?php var_dump( $wp_query->posts ); ?></pre><?php

This will output the array of posts retrieved by the main query on that specific page

This makes custom queries very ineffecient when is replaces the main query. It is like replacing an inflated tyre which is in tip top shape with a punctured, worn tyre.

YOUR SCENARIO

You have the following scenario

  • 119 posts from your custom post type

  • Posts per page option set in admin is 6

  • Custom query is set to retrieve 4 posts per page

THE MATHS

Your main query will return 20 pages which you can test with echo $wp_query->max_num_pages;. The maths is easy, you have 119 posts, which you can also check with echo $wp_query->found_posts;, posts per page is set to 6 in admin, so the ceiling of 119 / 6 = 20

As for the custom query, you can access the same info as above by changing $wp_query to $custom_query. You will see that you still have 119 posts, but you will have 30 pages due to the fact that posts per page is now set to 4. The ceiling for 119 / 4 = 30

404 ON PAGE 21

Whenever there are no posts to display by the main query, a 404 is triggered, and this is what you are seeing. There are enough posts to fill 20 pages, not 21 or more. So when go to page 21, the main query 404’s simply because there are no more posts to display regardless if you still have plenty to display from your custom query.

I have seen posts where people say the solution is easy, simply change the amount of posts per page in the admin page to match that of your custom query. Don’t ever do that. Yes, it does fix the issue, but it defeats the real issue here that you should not replace the main query with a custom one. And BTW, the real solution to the problem is pretty simple, clean and is the proper correct method to achieve what you need

WHY THE CUSTOM QUERY

You have decided to go with a custom query because of two things

  • Different amount of posts per page on your custom post type archive page that the rest of the site

  • Have posts sorted by date modified and not the default post date

As you can see, you have solved two issues with your custom query but have created other problems

THE SOLUTION

This the correct, simple method to solve your issue. Use pre_get_posts to alter the main query before it is executed. pre_get_posts alters the query variables accordingly which is used to calculated the SQL query for the specific page request.

You have to do two things here, the first would be to remove your custom query on your archive page and replace it with the default loop. Once you have done that, you will see 6 posts per page ordered by post date as it should be by default.

Now, open functions.php and use pre_get_posts to correct the amount of posts per page and sort order

add_action( 'pre_get_posts', function ( $q ) 
{
    if(    !is_admin() 
        && $q->is_main_query() 
        && $q->is_post_type_archive( 'projekte' ) 
    ) {
        $q->set( 'posts_per_page', 4          );
        $q->set( 'orderby',        'modified' );
    }
});

You should now see 4 posts per page ordered by modified date, correctly paged on your custom post type archive page projekte

EXTRA READING

You should also read the following post I had done on a similar issue a while ago, and all the posts I have linked to in that specific post

EDIT

For those stuck with the dinosaur php versions which does not support closures, here is the fall back version of the pre_get_posts action

add_action( 'pre_get_posts', 'wpse176347_pre_get_posts' );
function wpse176347_pre_get_posts( $q ) 
{
    if(    !is_admin() 
        && $q->is_main_query() 
        && $q->is_post_type_archive( 'projekte' ) 
    ) {
        $q->set( 'posts_per_page', 4          );
        $q->set( 'orderby',        'modified' );
    }
}

Leave a Comment