I'm aware this is a topic discussed to death, but I have read through many solutions and can't understand why my pagination simply isn't working.

I have a very simple custom wp_query:

$current_page = (get_query_var('paged')) ? get_query_var('paged') : 1;

$project_query = new WP_query(array(
  'post_type' => 'project',
  'paged' => $current_page,
  'posts_per_page' => 50

There are about 2000 projects in there, so without a doubt, there are pages.

This is on my index.php of the theme.

No matter what, the URL /p/pageno returns a 404. The function next_posts_link('Older Entries »', $project_query->max_num_pages); is always empty.

I have tried, based on reading several similar questions:

  1. Setting reading settings to display 1 post max at a time, to ensure pagination kicks in.
  2. Installing wp_pagenavi - same result
  3. Setting the homepage to a static page with this code in a custom page template - this finally works somewhat (/p/2 etc don't 404), but then next_posts_link() stops at page 2 and goes no further.
  4. change get_query_var('paged') to get_query_var('page') in the custom page template
  5. rename $project_query to $wp_query

I'm at a loss, this should be so simple, but pagination always seems to be the one thing that kills me when working with Wordpress.

  • "This is on my index.php of the theme" - why? The index.php file is the default fallback template for all other template files. Can you better explain what your end-result objective is? In what specific context are you intending to output this custom query loop? Commented Dec 17, 2013 at 17:08
  • @ChipBennett I am using index.php to affect the homepage. I understand I could set it as a page template, and use a static page as my homepage, I tried it (attempt #3 in the question above). I am basically trying to list all of the posts of the custom post type 'project' on the homepage.
    – waffl
    Commented Jan 7, 2014 at 12:00
  • But index.php is the incorrect template file to use to impact the site front page specifically. If you want to impact the site front page, use front-page.php. If you want to impact the blog posts index, use home.php. Refer to the template hierarchy. Commented Jan 7, 2014 at 13:07

2 Answers 2


I think most pagination issues stem from a misunderstanding of the query and template loading process that happens before you are executing code within a template file.

When someone requests a page:

  1. WordPress parses the request and decides what it's for based on the incoming URL. For example, is it the main root page, does it begin with category, does it match the pattern of a single post, does it have a page number, etc.. The correct query vars are set from this process.

  2. WordPress runs the main query on the database, using the query vars set in the last step.

  3. WordPress looks at the results of the main query, and determines whether it was successful or produced no results. If it was successful, WordPress loads the correct template for that type of request, or loads the 404 template if it wasn't.

So now you're finally in the template, if everything above went as expected. But now we can see the source of one problem - if that main query produced no results, you'll never reach the template to run your custom query. That main query has no relation to the custom query in your template. Whether or not your custom query has results for that page number is irrelevant, if it doesn't have a corresponding page in the main query. The answer in this case, is to modify the main query before it happens via pre_get_posts, to make that query give us the results we want instead of running another query in the template.

The other option, which you mention in your question, is to use a static page. This is the exception to the above behavior regarding the main query - a static page will return that single page's content regardless of what page number you set in the URL. This is how you can get around the 404 issues with main queries. In that case, you must reference the correct query object when using the next/previous posts link functions, as Kaiser mentions in his answer.

  • I'm sorry for the delay in responding. You are definitely right, which is why I tried with a static page. As mentioned above, I mistyped my question and was using the correct query in my code. I now have a static page that is able to reach page 2, but goes no further. get_next_posts_link always returns a link to page 2 even though $project_query->max_num_pages returns 69. As a quick side question, I could alter pre_get_posts like this example, is it better practice?
    – waffl
    Commented Jan 7, 2014 at 12:15
  • As a note, manually entering the page in the URL http://domain/page/3/' does work. So I'm not sure why get_next_posts_link('Older Entries »', $project_query->max_num_pages) is always returning to a maximum to page 2.
    – waffl
    Commented Jan 7, 2014 at 12:50
  • OK. Well, I decided to alter the loop with pre_get_posts and it is working just perfectly with infinite scrolling and everything! :)
    – waffl
    Commented Jan 7, 2014 at 13:10

Your main problems are:

  • You got no Error reporting/debug turned on: WP_Query vs. WP_query...
  • Inside your posts link call, you're referring to the main query instead of your custom query.

Look at this:

$project_query = new WP_Query( array(
  'post_type'      => 'project',
  'paged'          => get_query_var('paged') ? get_query_var('paged') : 1,
  'posts_per_page' => 50
) );
var_dump( get_next_posts_link( "Older Entries →", $project_query->max_num_pages ) );
  • I'm sorry for the delay in responding. I've altered WP_query to WP_Query and unfortunately, I had mistakenly typed the main query here in my question, but my code was using the correct one. Unfortunately, the var_dump is still empty.
    – waffl
    Commented Jan 7, 2014 at 11:59
  • Edit: the var_dump does indeed return a link to the next page, but this next page is a 404.
    – waffl
    Commented Jan 7, 2014 at 12:06
  • @waffl If the next page is a 404, then your query has a problem :)
    – kaiser
    Commented Jan 7, 2014 at 21:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.