WordPress Rewrite API: How URL Requests Map to Content
Master the WordPress Rewrite API to gain total control over your site's URL structure. Learn how WP_Rewrite maps requests to content without the headache.

I remember sitting at my desk three years ago, staring at a "404 Not Found" page for an hour because I’d tried to force a custom URL structure into a client's site using simple $_GET parameters. I thought I was being clever, but I was just fighting the system. Once I finally sat down and actually dug into the WordPress Rewrite API, I realized that URL routing isn't magic—it's just a sophisticated regex parser.
When a user hits your site, WordPress doesn't just look for a file named about-us.php. It looks for a match in its rewrite rules. If you've ever wondered how WordPress permalinks work under the hood, you’re essentially asking how the global WP_Rewrite object converts human-readable strings into the database queries that WP_Query Explained: How WordPress Fetches Your Content eventually executes.
The anatomy of a URL request
At the heart of everything is the WP_Rewrite class. When you change your permalink settings in the dashboard, WordPress updates the rewrite_rules option in your database. This is a massive associative array where the keys are regular expressions and the values are query strings.
Think of it like this:
- Request: A user visits
example.com/portfolio/web-design/. - Matching: WordPress iterates through the
rewrite_rulesarray. It finds a regex that matchesportfolio/([^/]+)/?. - Parsing: It extracts the slug (
web-design) and maps it to a query variable, usually something likeportfolio_cat=web-design. - Execution: WordPress sets the global
$wp_queryobject based on those variables and fetches the content.
We first tried to build a custom routing system by hacking the .htaccess file directly. It felt like a good idea until we realized that every time we pushed a plugin update or a user saved their permalink settings, WordPress wiped our manual rules. It took about two days of refactoring to realize we should have been using add_rewrite_rule() all along.
Implementing custom routing

If you need to create custom URL structures—like /events/2023/october/—you don't need to touch the server configuration. You can do it all through the API.
Here is a basic example to register a custom rule:
PHPfunction my_custom_rewrite_rules() { #6A9955">// Map /portfolio/%name%/ to index.php?portfolio_slug=%name% add_rewrite_rule( '^portfolio/([^/]+)/?', 'index.php?portfolio_slug=$matches[1]', 'top' ); } add_action('init', 'my_custom_rewrite_rules'); function my_custom_query_vars($vars) { $vars[] = 'portfolio_slug'; return $vars; } add_filter('query_vars', 'my_custom_query_vars');
Crucial note: After you add this code, you must flush your rewrite rules. You can do this by visiting the Permalinks settings page in the dashboard. Don't run flush_rewrite_rules() on every page load; it’s an expensive operation that hits the database and rebuilds the entire rules array.
.htaccess generation and server-side logic
While the API handles the logic, WordPress still needs to hand off the request. On Apache servers, WordPress uses .htaccess generation to force all traffic through index.php.
If you're hosting on NGINX, you don't get an .htaccess file. Instead, your server configuration (or Kubernetes Ingress: NGINX vs Gateway API for Traffic Routing) must be configured to pass requests to the PHP-FPM processor. WordPress generates the rules, but the web server performs the initial interception.
Common pitfalls and debugging

- Rule Order: Using
'top'as the third argument inadd_rewrite_ruleis usually safer. It places your rule at the beginning of the array, ensuring it’s checked before WordPress's default rules. - Flush Issues: If your new URL isn't working, it’s almost always because the rules haven't been flushed.
- Conflict: If you create a rule that conflicts with a page slug, WordPress might get confused. Always check if a Page or Post exists with the same URL before debugging your regex for three hours.
I’m still occasionally tripped up by complex regex patterns. If I’m dealing with something particularly hairy, I use a plugin like "Rewrite Rules Inspector" to visualize the array. It saves me from guessing which rule is actually catching my request.
Next time, I’d probably spend more time validating the input parameters before they hit the database. It’s easy to get the URL routing working, but sanitizing the $matches from your rewrite rules is a step most developers skip until it's too late. The WordPress Rewrite API is powerful, but like any engine, it needs to be maintained.
Frequently Asked Questions
Do I need to flush rewrite rules every time I change my code? Yes, but only during development. Use the "Permalinks" settings page in the dashboard to trigger a flush manually.
Why is my custom URL returning a 404?
Usually, it’s because the query_vars filter wasn't updated to recognize your custom variable, or the rewrite rules haven't been flushed.
Can I use the Rewrite API for REST routes? While you can, you shouldn't. If you're building a headless application, Extending the WordPress REST API with Custom Endpoints is a much more robust and standard way to handle data fetching.