WordPress rewrite’s and pretty URL’s

For the longest time I chose to ignore WordPress rewrite’s and the ability to craft my own custom URL’s.

Why? Because customising them just looked hard, and the built-in facilities were pretty good for my needs.  After all, blog entries were stored under the path “/blog”, products under “/products” and so on. It worked pretty well, and I could even customise this to some extent in the back-end of WordPress.

When it all changed

Only in recent years, as I began to realise the impact that a site’s URL structure could have from an SEO standpoint did I decide to dive head first into the murky world of WordPress Rewrite rules. I’ll cover this in more detail at a later date, but in short, the relative depth of a URL can affect the perception of value of that URL to a search engine. A rudimentary example would be to compare mywebsite.com/products/categories/category-one/sub-category-one/product-name with  mywebsite.com/about-us. It’s pretty clear that the latter is “higher up the food chain”, when it comes to importance. And in a small way, this is how a search engine sees it as well.

For years I was also frustrated at the seemingly silly requirement to prefix my WordPress post-types with a unique URL. I now know the impact that this has on database performance (significant on larger sites), and so I’ve stopped complaining albeit still frustrates me. But even recently, I would create a custom post type and a taxonomy, and just “accept” the fact that my URL structure was pre-determined.

So there have been times when planning complex categorical structures for an SEO optimised site where we wanted a better way of crafting our URL’s, and other times when we simply wanted the path to “make more sense”.

So when I began learning how this all works in the WordPress eco-system, I have to admit I was confused. But like all lightbulb moments, the day it became clear was a joyous one.

How do WordPress rewrite’s actually work?

The ease with which these can be adjusted (programatically of course) took a little time to learn, but I found the biggest hurdle to understanding it all was knowing the components in play. Then came the need to learn regular expressions, however I’ll get to that shortly.

In a nutshell, wordpress rewrites have two moving parts.

  1. Take a “pretty” URL and translate this into the complex structure needed (e.g. index.php?something=foo&nice=bar) to tell WordPress exactly what’s being asked of it
  2. Convert the complex structures above into pretty URL’s through the entire website so that links don’t appear ugly

I know this seems obvious, but so many times I forgot to differentiate between these two parts, and subsequently got quite confused.

Translating the pretty URL into something usable

This is where the fun started.

I can honestly say I would have spent one quarter of the time understanding how this part works had I learnt to write regular expressions properly first, rather than as an afterthought. As it turned out, I learnt using the assistance of a great resource that helps debug a regular expression – https://regex101.com/.

To convert a pretty URL into a format that WordPress can use, we can use the function called add_rewrite_rule(). I’ll use an example we’ve implemented on this website. We wanted our blog to use the word “learn”, and have all blog entries and blog categories stored under this URL. Now we could have simply renamed our blog base in our WordPress Permalink Settings, but that wouldn’t force our URL’s to include the category in the URL at all times. We’d finish up with a URL that looks like this – https://21designs.com.au/wordpress-rewrites-and-pretty-urls/.

So we decided to avoid the Permalink Settings altogether and write a function that forces WordPress to recognise the structure we wanted.

Important Note: Remember, this part does not affect the way WordPress spits out a URL, only the way WordPress interprets one that you give it.

As you can see, we’re using the ‘init‘ action hook to define our rewrite rule. We could have several rewrite rules in this one function but for this example we’re showing only one. The rewrite rule itself follows this format…

add_rewrite_rule(
    regular expression,
    resulting url,
    position
);

Let’s start with our regular expression. This is what we use to match up the URL that was actually requested – in this case, we’re looking for something like /learn/designer-tips/page/2/.

^/?learn/([^/]+)/page/([0-9]+)/?$

We’re basically saying here that if the URL begins with learn, has some stuff, then has another slash, followed by the word ‘page’, a slash and finally a number – then we’ve found a match (it’s a bit more complex than that but you get the point)!

The second line tells WordPress what to do with the match once it’s been found. In our example, we’re telling WordPress to load the page where the category_name matches the first regex variable – ([^/]+). We’re also telling WordPress that the results should be paginated by including the paged query string, and of course we pass the second regex variable to achieve that – ([0-9]+).

Finally, we finish with the argument – top, which ensures our rewrite rule jumps the queue and slots into the very top of all our rewrite rules.

This last part is important, because if you’re like me, and your regex could be better, then you’ll invariably write rules that aren’t as specific as you’d like them to be resulting in potential conflicts. This is where the first rule will win, and is why the order of processing can become important.

Turning something usable into a pretty URL

So it’s all well and good to let WordPress understand a URL that’s passed to it, but how do we tell WordPress to output a URL the way we want to see it? That’s the second moving part – after all, it’s no good if all our URL’s are full of query strings throughout the site. So unless you’re sticking to the normal WordPress way of things, you’ll need to customise the URL’s when WordPress tries to display them on the front end as well.

The hooks you use might change, depending on whether the rule is modifying a custom post, a taxonomy or just a regular WordPress post but here’s an example of how you might do that for our example above.

What we’re doing here is hooking into the post_link filter which is executed before spitting out the link URL for any given post allowing us to manipulate it first.

We then check to see if the current item is in fact a post using if ( is_object( $post ) && $post->post_type == ‘post’ ) {. If it is, we use the native WordPress function get_the_terms() to find the primary category ($terms[0]->slug) and help form our URL to match the rewrite rule added earlier.

In this case we’re altering the output of a post, using the full category path to do so, but if you were looking to alter a category (without the post on the end) you’d use the category_link filter instead of the post_link filter. Other filters exist for custom post types, but you can find them quite easily in the WordPress Codex, for now, we won’t be covering them here.

It’s as easy as 1, 2, 3!

So assuming you have a grasp on regular expressions, and you’re comfortable working with action hooks and filters, there’s absolutely no reason to go and customise your WordPress rewrite rules to make your website more user friendly.

As you can see, we’re using the ‘init‘ action hook to define our rewrite rule. We could have several rewrite rules in this one function but for this example we’re showing only one. The rewrite rule itself follows this format…

add_rewrite_rule(
    regular expression,
    resulting url,
    position
);

Let’s start with our regular expression. This is what we use to match up the URL that was actually requested – in this case, we’re looking for something like /learn/designer-tips/page/2/.

^/?learn/([^/]+)/page/([0-9]+)/?$

We’re basically saying here that if the URL begins with learn, has some stuff, then has another slash, followed by the word ‘page’, a slash and finally a number – then we’ve found a match (it’s a bit more complex than that but you get the point)!

The second line tells WordPress what to do with the match once it’s been found. In our example, we’re telling WordPress to load the page where the category_name matches the first regex variable – ([^/]+). We’re also telling WordPress that the results should be paginated by including the paged query string, and of course we pass the second regex variable to achieve that – ([0-9]+).

Finally, we finish with the argument – top, which ensures our rewrite rule jumps the queue and slots into the very top of all our rewrite rules.

This last part is important, because if you’re like me, and your regex could be better, then you’ll invariably write rules that aren’t as specific as you’d like them to be resulting in potential conflicts. This is where the first rule will win, and is why the order of processing can become important.

Turning something usable into a pretty URL

So it’s all well and good to let WordPress understand a URL that’s passed to it, but how do we tell WordPress to output a URL the way we want to see it? That’s the second moving part – after all, it’s no good if all our URL’s are full of query strings throughout the site. So unless you’re sticking to the normal WordPress way of things, you’ll need to customise the URL’s when WordPress tries to display them on the front end as well.

The hooks you use might change, depending on whether the rule is modifying a custom post, a taxonomy or just a regular WordPress post but here’s an example of how you might do that for our example above.

What we’re doing here is hooking into the post_link filter which is executed before spitting out the link URL for any given post allowing us to manipulate it first.

We then check to see if the current item is in fact a post using if ( is_object( $post ) && $post->post_type == ‘post’ ) {. If it is, we use the native WordPress function get_the_terms() to find the primary category ($terms[0]->slug) and help form our URL to match the rewrite rule added earlier.

In this case we’re altering the output of a post, using the full category path to do so, but if you were looking to alter a category (without the post on the end) you’d use the category_link filter instead of the post_link filter. Other filters exist for custom post types, but you can find them quite easily in the WordPress Codex, for now, we won’t be covering them here.

It’s as easy as 1, 2, 3!

So assuming you have a grasp on regular expressions, and you’re comfortable working with action hooks and filters, there’s absolutely no reason to go and customise your WordPress rewrite rules to make your website more user friendly.