An Introduction to Umbraco UrlRewriting
If you have been working in the Umbraco environment for a while, you may have already honed your skills on this topic. If you haven't, then let me be the first to tell you that UrlRewriting in Umbraco is an incredibly handy feature that is incredibly easy to use. Be warned, however. Creating too many rules can have a negative impact on the performance of your site.
The UrlRewriting feature used in Umbraco is actually a project developed by Thomas Bandt and Albert Weinert called UrlRewritingNet.UrlRewrite. It is an open source project housed on GitHub and has proven to be an effective tool for managing URL redirections in .NET web applications. Its power lies in its implementation as an HTTP module, which provides developers with a fair amount of control in their web applications. The downside is that HTTP modules are executed for every request that comes in to your site, which includes scripts and images.
So, what exactly is a URL rewrite? In basic terms, it is a translation that occurs on the server of the incoming URL to a different URL that has more meaning to the application. For example, let's say you have a blog and your user clicks on a category link. Your SEO-friendly URL may read:
If your site was actually configured in this manner, your Umbraco content tree would have several category nodes under a Category parent. That's not a very effective design. A better approach would be to simplify your content structure and rewrite the URL as:
But, that's not friendly for your users or search engines. Using UrlRewriting, your application can present SEO-friendly URLs, like the former, then rewrite it such that the app sees the latter. Your application would be able to read the category parameter from the query string and filter the list of articles by the category named "umbraco". The end result is that you have a visible URL with meaning and a server-side translation that allows your application to be more structured and efficient.
The URL rewrite implementation in Umbraco works by matching the incoming URL to rules defined in the UrlRewriting.config file (located in the config folder) using regular expressions. Here is a sample, based on our previous example:
<add name="CategoryRule" virtualUrl="^~/blog/(.*)/(.*)" destinationUrl="~/blog.aspx?$1=$2" rewriteUrlParameter="ExcludeFromClientQueryString" ignoreCase="true" />
Let's break this down a bit:
A regular expression that matches the incoming URL. The caret (^) tells the expression engine to start looking at the start of the string. The tilde (~) is the application root operator in .NET, which completes the virtual URL. The final part of the expression, (.*), captures all characters after the slash into an expression group. Groups are stored as a zero-based array, where the entire string is the first entry at index 0. Here, "category" would be stored at index 1 and "umbraco" at index 2.
A regular expression that represents the target path on the server. In this case, the groups captured in the virtualUrl are placed here using $1 and $2, representing their index positions in the groups array.
Tells the module how to handle URL parameters. In most situations, ExcludeFromClientQueryString is the preferred method.
Query string parameters are still accessible via Request.QueryString. However, any parameters added to the destinationUrl property will not be added to Page.ClientQueryString.
Any parameters included in the URL, as well as parameters defined in the rewrite, are added to the HttpContext.Current.Items array.
The entire query string will be included in the rewrite process.
Tells the module to use case-insensitive pattern matching on the URLs.
Awesome! Rewrite all the URLs! Well, remember the earlier warning. This HTTP module is called for every request to the server, whether it needs a rewrite or not. In addition, each request is compared to each of the rewrite rules defined in UrlRewriting.config. The more rules you have, the more work your app will need to do on each request. The good news is that it is designed to stop processing once it finds a match. The bad news is that most requests are likely not going to match any of the rules, which means a lot of processing. So, here are a few tips that may help:
- Don't create multiple input rules on a common destination. One well-written regular expression can do the trick.
- Do try to order your rules so that the most common are at the top. The module will find what it needs quickly and move on.
- Don't leave unused rules behind. If it's not needed, get rid of it to cut down on unnecessary processing.
- Do use techniques such as caching and bundling to reduce the overall number of requests. This is good practice even if you don't use UrlRewriting.
As you can see, there is a bit of effort involved in creating effective URL rewrites. However, once configured, they can provide for a much cleaner and memorable experience to your users. Any time you have a need to use a URL that provides meaning - or, if you simply want to hide those ugly query string parameters - the URL Rewriting module is the easiest place to start.