Rewrite rules are how WordPress creates clean / pretty URIs from URL query parameters. When your new page or blog post automatically gets a human-friendly URL, this is provided by a rewrite rule, which itself is using WordPress’s Rewrite API.
In this post I hope to cover the basics of the Rewrite API, create a few new rewrite rules as examples, and make use of the data the Rewrite API provides the global WP_Query object when it matches a rewrite tag. Let’s get started!
How does URL rewriting work?
You’ve probably heard of “redirects” before. This is when one URL sends you to another URL. In your browser, the URL in the location bar will actually change to reflect the new URL you have been redirected to.
Rewrites are similar to redirects, but they are done behind the scenes so that the URL in your browser’s location bar does not change. This should become more clear by examining how you can configure your web server to handle rewrites.
The web server does the rewrite
For rewrites to work in any system, the web server must be configured correctly. For Apache web servers, this is most-easily achieved in the form of an
.htaccess file. This file tells Apache to take the requested URL and serve it as a URI to the
Let’s see what this looks like:
First, the configuration is checking to make sure the Apache rewrite module is enabled. Then, it makes sure that the requested URL is not a real file or directory within the codebase. And finally, it rewrites the requested URI to the
Example: If you were to visit
example.com/some-pretty-url, behind the scenes Apache would deliver that URI to the index.php file, resulting in
example.com/index.php/some-pretty-url (behind the scenes).
For all intents and purposes, this is now acting as a Front Controller. Meaning that you now have a single point of entry for requests into your application. That single point of entry is your
index.php handles the request
Now that your requests are all going through
index.php, you can write a script within that file to look for the request, and handle it appropriately. Here is a very simple example of how that might be done using a pattern matching strategy similar to that you’ll find in WordPress:
Note that the global
$_SERVER variable contains a key called ‘REQUEST_URI’. This is how you get the URI that was requested by the visitor:
$_SERVER['REQUEST_URI']. Simple huh?
Next, this script attempts to match the requested URI against a pattern of
post/(some value), and if it is found, returns a query-like string with the found key-value pair.
For example: If we were to visit
example.com/post/1234, the pattern would find a match and return a string of
Then the script parses that query-like string into an array named
And finally, if the ‘post’ key is found in
$query_vars, the script alters the response accordingly.
Rewrites in WordPress
The WordPress rewrite mechanisms work in much the same way. They provide an API through the use of simple functions, and those functions tend to expect either a regex (regular expression) pattern, rewrite string, or both. Let’s take a look at 3 of those API functions.
Adding rewrite rules requires both a regex pattern, and resulting rewrite string. Just like our above example, WordPress will use the regex pattern to attempt to match the requested URI; and when a match is found it will convert the found values into the defined rewrite string.
Let’s look at a simple example:
Using the above example along with a real post slug from your site, you could visit the URL
example.com/post-by-slug/hello-world and see the content from the default “Hello World” post that comes with a fresh WordPress install.
It achieves this by rewriting the data found in the url after
/post-by-slug/ to the core “name” rewrite tag, so that
post-by-slug/hello-world is rewritten to
index.php?name=hello-world. Later in the page load, when WordPress finds that the “name” query variable has a value, core mechanisms take over and serve the post associated with that name (aka, post_name or slug).
Adding a rewrite tag allows you to provide custom variables to the global $wp_query object. When you add a rewrite tag to WordPress, you are informing the system that this variable is to be expected, and available through the common query API functions such as
get_query_var('some-tag'), or directly from the object
The main reason to use a rewrite tag is to allow a URL variable to be stored in the global $wp_query object. This lets you access it from any part of the site using
One example use case might be that of tracking visits to your site. For example, if you wanted to track affiliate links to products on your site:
In this example a cookie will be set for any user who visits a url with an
The reason to use a rewrite_tag alone is that it does not impose a structure on the URL. It only tells WP_Query to expect a variable of the given name.
Next, let’s combine the use of
add_rewrite_tag to a new reliable application route!
You may have heard of URL shorteners before. They’re great because they make URLs easier to share online through sites and services that have strict character limits.
But have you ever considered a URL lengthener? I know I haven’t. But none-the-less, I find great joy in creating impractical examples, so here we are.
In this example we create a new rewrite rule and rewrite tag.
The rewrite rule tells WordPress, “Expect a URI that starts with
longerer/, and rewrite it to the URL variable named
The rewrite tag tells WordPress, “Keep a lookout for a URL variable named longerer. If you find it, please put it along with its value into the global $wp_query.”
Then using the template_redirect hook, we look for our new tag as a query_var (
get_query_var( 'longerer' )), decode it to find the post ID. If the post ID is legitimate, we redirect to that post; otherwise redirect to the home page.
An “endpoint” is a partial URI that can be appended to another URI to produce an alternate result or provide additional data. For example, if you had a post located at
example.com/post/hello-world and wanted to allow other parties to retrieve that post data as json, you could create an endpoint located at
example.com/post/hello-world/json and return json when that endpoint is visited.
Adding endpoints to your WordPress install might be the easiest way to use the Rewrite API, as it doesn’t require any regex or fancy formatting.
Endpoints can also receive values appended to them in the form of another path argument in the URL. Let’s create a few endpoints.
This snippet provides an endpoint named “debug” that expects a value of “post” or “query”, and shows debugging information about the global $post or $wp_query objects accordingly.
Note that I have used the
EP_ALL bitmask because I want this endpoint to be available on all pages of my WordPress site.
JSON endpoint for posts and pages
Considering the previously mentioned example of a JSON endpoint, let’s see what this would look like in WordPress:
This is as simple as it gets. We use
add_rewrite_endpoint to create a new expectation for an endpoint named “json”, and we tell it to only work on permalinks (individual posts) and pages.
template_include hook, we look to see if our endpoint is being accessed, and return the
$post object as json to the visitor.
To see the available
EP_* masks, see the
add_rewrite_endpoint codex page.
Rewrite Tag for Permalinks
This final example is to show how you can use a rewrite tag to provide a new permalink structure for your site. Similar to the URL Longerer example, this uses both a rewrite rule and a rewrite tag to set expectations for both URIs and query variables.
Additionally this task will need to do a few other things, let’s take a look:
Using both the
post_type_link hooks, it alters permalinks as they are presented on the site by replacing our rewrite tag with the value found in the query variables. It also uses
pre_get_posts to provide a fallback mechanism for any post that does not have a meta data with the key
I am not really a big fan of this example because it seems a bit hacky for my tastes. Ideally, if you need to provide a new permalink structure using a custom rewrite tag, you provide it for a piece of data you can reliably expect each post to have.
Refreshing rewrite rules
It’s important to note that WordPress caches rewrite rules. This means that when you change your plugin’s rewrite code, you probably need to refresh that cache to pick up your changes.
One way to do that is to visit your site’s Dashboard > Settings > Permalinks page, and “Save Changes”.
Programmatically, you can use the
flush_rewrite_rules function. If you’re writing a plugin that implements the Rewrite API, chances are you want to flush rewrite rules on plugin activation.
One last thing
I’d like to reiterate again that these examples are not complete and should not be used as-is. That being said, I hope you learned something useful; and if you see any mistakes in this post, please let me know in the comments below.
- Gist for all these examples
- A (Mostly) Complete Guide to the WordPress Rewrite API
- add_rewrite_endpoint – codex (includes list of bitwise masks)
- flush_rewrite_rules – How to refresh rewrite rules programmatically
- Helper Class To Add Custom Taxonomy To Post Permalinks
- Query Monitor – a plugin for debugging all things query related
- Rewrite API codex
- WordPress for the Adventurous: Rewrite API