Back to Community

Why Your Custom WP_Query Pagination Fails on Live Sites (And How to Fix It)

36 threads Sep 7, 2025 CoreDeveloping with wordpress

Content

If you've built a custom query using WP_Query for pagination, you might find it works perfectly on your local development environment but breaks when you move it to a live server. Clicking to page 2 results in a "Page Not Found" error. This is a common and frustrating issue for WordPress developers. This guide will explain why it happens and walk you through the most reliable solutions.

Understanding the Problem

The core of the issue often lies in how WordPress handles main queries versus secondary queries. The main query, which runs before any templates are loaded, is responsible for parsing the request URL and determining what content to show. Pagination parameters like paged are part of this main query.

When you create a custom WP_Query for a page like home.php or a custom template, you are creating a secondary query. The pagination functions you use, like paginate_links(), are built to work with the main query's data by default. If your secondary query uses different parameters (like a different post_type), the main query's pagination data becomes irrelevant, causing conflicts and broken links on the live site where URL rewriting is active.

Common Solutions

1. Use pre_get_posts to Modify the Main Query (Recommended)

The most robust method is to avoid a custom WP_Query for primary post lists and instead use the pre_get_posts action hook to modify the main query before it executes. This ensures all native pagination functions work correctly.

function my_custom_main_query( $query ) {
    // Only modify the main query on the front-end homepage
    if ( ! is_admin() && $query->is_home() && $query->is_main_query() ) {
        $query->set( 'post_type', array( 'arizona_wine', 'arizona_beer' ) );
        $query->set( 'posts_per_page', 12 );
        $query->set( 'orderby', 'title' );
        $query->set( 'order', 'ASC' );
    }
}
add_action( 'pre_get_posts', 'my_custom_main_query' );

With this approach, you can then use the standard loop in your home.php template without creating a new WP_Query object. The pagination will just work.

2. Fix the Pagination Base in Your Custom Query

If you must use a custom WP_Query, you need to ensure the paginate_links() function generates the correct URLs. The problem often stems from the 'base' argument. The common snippet using str_replace($big, '%#%', get_pagenum_link($big)) can fail on some server configurations.

A more reliable method is to use esc_url( get_pagenum_link() ) to build the base URL.

$pagination = paginate_links( array(
    'base'      => esc_url( get_pagenum_link() ) . '%_%',
    'format'    => '?paged=%#%',
    'current'   => max( 1, get_query_var( 'paged' ) ),
    'total'     => $all_posts->max_num_pages,
    'prev_text' => 'Previous',
    'next_text' => 'Next',
) );

if ( $pagination ) {
    echo '';
}

3. Check Your .htaccess and Permalink Settings

Sometimes, the issue is not your code but your server's configuration. A missing or corrupt .htaccess file can prevent URL rewriting from working, which breaks pagination.

  • Go to Settings > Permalinks in your WordPress admin.
  • Simply click "Save Changes" without making any modifications. This refreshes the rewrite rules and often regenerates the .htaccess file.
  • Ensure your web server (e.g., Apache) has the mod_rewrite module enabled and that the .htaccess file is writable.

Conclusion

Pagination issues with custom queries are a classic WordPress development challenge. The most sustainable fix is to use the pre_get_posts hook to alter the main query, as this keeps you within the standard WordPress lifecycle. If a custom query is unavoidable, paying close attention to how you generate the pagination base URL is critical. Finally, never forget to reset your permalinks after moving a site to a new server.

Related Support Threads Support