As a web developer, there are few things more frustrating than a bug that “fixes itself” only to break again the moment you look away. Last week, I ran into a classic WordPress headache: Custom Post Type pagination throwing 404 errors. I’d go into Settings > Permalinks, hit “Save,” and everything would work perfectly. Five minutes later? Page 2 was a 404 again.
After years of building complex WordPress architectures, I know that when “saving permalinks” is the temporary cure, the disease is usually Rewrite Rule Corruption. If you’re seeing this on your site, you aren’t crazy—WordPress is just losing its map.
Here is exactly what I found during my deep dive and how I fixed it for good.
The “Why”: Why does it break?
The most common culprit is Rewrite Rule Corruption. WordPress stores a “map” of your URL structure in the database. When a plugin updates, a new post is saved, or a certain hook is triggered, that map can get scrambled. If your CPT configuration isn’t perfect, WordPress loses the path to your paginated pages.
3 Steps to Permanent Fixes
1. Avoid the “Slug Clash”
In my experience, this is the most frequent oversight. WordPress gets confused if a Page and a Custom Post Type share the same slug.
- The Error: You have a page named
projectsand a CPT registered with the slugprojects. - The Fix: Make sure your CPT archive slug is unique. If you need them to be the same, you must set
'has_archive' => trueand ensure your rewrite rules are specific.
2. The posts_per_page Mismatch
This is the “silent killer” of pagination. If your WordPress global settings (Settings > Reading) say show 10 posts, but your custom template query says show 5 posts, the math fails.
- The Logic: When you visit
/page/2/, WordPress checks the global setting. If you only have 8 posts total, WordPress thinks page 2 shouldn’t exist because $8 < 10$. It serves a 404 before your custom template even has a chance to run its query. - The Fix: Use the
pre_get_postshook infunctions.phpto tell WordPress exactly how many posts to expect before the page loads.
3. Stop Manual Flushing
I’ve seen developers put flush_rewrite_rules(); directly in the init hook. Don’t do this. * The Impact: It forces WordPress to rebuild its entire URL map on every single page load. This leads to race conditions where the rule isn’t ready when the page requests it, causing those intermittent 404s.
The Code Solution
I recommend adding this to your functions.php file. This aligns the WordPress core “math” with your custom display requirements:
PHP
function fix_my_cpt_pagination( $query ) {
// Only target the main query on the front-end for your specific CPT
if ( !is_admin() && $query->is_main_query() && is_post_type_archive('your_cpt_slug') ) {
$query->set( 'posts_per_page', 10 ); // Match this to your design
}
}
add_action( 'pre_get_posts', 'fix_my_cpt_pagination' );
Summary Checklist
- [ ] Unique Slugs: Is your CPT slug different from your Page titles?
- [ ] Query Sync: Does your
posts_per_pagematch your Reading settings? - [ ] No Auto-Flush: Did you remove
flush_rewrite_rulesfrom your active code? - [ ] One Last Save: Go to Permalinks and hit “Save” one final time.
Expert Tip: If you’re using an SEO plugin, check if “Strip Category Base” or “Clean Permalinks” is active. These features often “clean” the pagination rules right out of your database.


