WordPress rewrite rules dictate how your site maps URLs to content. Learn how the WP_Rewrite engine transforms permalinks into regex for efficient routing.
Last week, a client asked why their custom post type wasn't showing up after a migration, and it sent me straight back into the heart of the WP_Rewrite class. If you've ever wondered how a URL like /blog/my-post/ actually triggers a database query for a specific post, you’re looking at the silent workhorse of the WordPress routing engine.
Understanding WordPress rewrite rules isn't just for plugin developers. It’s for anyone who wants to debug why their site is throwing 404s or why a custom URL structure feels sluggish.
At a high level, WordPress doesn’t have real pages in the filesystem for your posts. Instead, it uses a massive array of regex patterns. When a request hits your server, WordPress compares the requested URL against these patterns. If it finds a match, it sets global query variables, and the template hierarchy takes over.
When you save your permalink settings in the dashboard, WordPress triggers a rebuild of these rules. It iterates through all registered post types, taxonomies, and custom rewrite structures to compile the $wp_rewrite->wp_rewrite_rules() array.
If you want to see what’s happening under the hood, I recommend dumping the global $wp_rewrite object. You can peek at the current ruleset by running this:
PHPglobal $wp_rewrite; echo '<pre>'; print_r($wp_rewrite->wp_rewrite_rules()); echo '</pre>';
You’ll see a list where the key is a regex string and the value is a query string. For example:
(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$ translates to index.php?pagename=$matches[1]&feed=$matches[2].
The $matches array is the secret sauce. WordPress uses PHP’s preg_match to capture parts of your URL and map them directly to WP_Query parameters.
When we talk about WordPress rewrite rules, we’re essentially talking about a performance-sensitive lookup table. Every time a visitor lands on your site, WordPress has to iterate through these rules. If you have hundreds of custom rules, your site’s initial request time—often around 250ms to 400ms on a decent server—can bloat significantly if you aren't careful.
I once worked on a project where a developer added a custom rule inside a loop, causing the WP_Rewrite object to grow to over 500 entries. The site slowed down by nearly 1.5 seconds just on the routing phase.
Before you start adding custom rules, I suggest reviewing WordPress Rewrite API: How URL Requests Map to Content to ensure you aren't reinventing the wheel. If you're struggling with routing, understanding WordPress URL rewriting: How parse_request maps your site permalinks is the next logical step to see how those regex matches turn into actual database queries.
We’ve all been there: you add a new rewrite rule, and nothing happens. It’s almost always because the rules haven't been flushed. WordPress caches the rewrite array in the database option rewrite_rules.
You might be tempted to call flush_rewrite_rules() in your init hook to fix this. Don't. It’s an incredibly heavy operation. If you want to understand the performance impact of doing this wrong, check out WordPress Rewrite API: Managing Rules and Flush Failures. It covers the "why" behind the performance hit much better than I can here.
Instead of hacking the core, I prefer to use a small helper function to check if my regex is actually catching the URL.
PHPadd_action('template_redirect', function() { global $wp_query; if (is_404()) { #6A9955">// Log that we missed the regex match } });
If you find that your regex is being ignored, check your priority. Rules added later in the process often override earlier ones, or the built-in WordPress rules might be catching your request first.
I’m still not a fan of how opaque the regex generation process can be. It feels like a black box, and sometimes it is. Next time you’re debugging a 404, stop looking at your templates and start looking at the rewrite rules.
If you're still curious about how these rules interact with the rest of the application, looking into WordPress rewrite rules: Deconstructing the flush_rewrite_rules process will give you a deeper look at why the persistence of these rules is both a blessing and a curse.
FAQ
add_rewrite_rule() with a high priority, and always ensure you are using the rewrite_rules_array filter if you need to modify existing core behavior.Master the WordPress Rewrite API and learn why calling flush_rewrite_rules incorrectly ruins performance. Get expert tips on managing WordPress permalinks.