Even with the plethora of routing options available in ASP.NET, there are still times when you want to manipulate a URL and would prefer to do it outside of code. When that’s the case, your best bet is most likely going to be to use IIS’s Rewrite Module.
Why can it be useful to keep rewrites/redirects out of code? Being able to push various URL transformations out of code and into config can give you the flexibility to do a few things in a much cleaner and easier way:
- Perform transformations that you’d only ever want to happen in production, such as ensuring all users access your site via the www URL, and forcing HTTPS for various pages, without needing to resort to polluting your codebase such as “if(HOSTNAME == “localhost”)”
- Perform redirections for old or moved content, again, without the need to pollute your code
- Easily implement SEO tweaks and optimizations quickly and easily (such as domain specific robots.txt files for a codebase which has bindings to multiple domains), again, without code, to make it easy to make revisions via config, so a deployment isn’t needed whenever any change is made.
The IIS Rewrite Module is easy to use, however for some rules it can take a bit of trial and error to get your syntax correct. After using it for many projects across multiple customers, I thought it might be useful to put up a post containing a small collection of the useful rules I’ve come across, for your copying and pasting pleasure.
This post will be a living document, and I’ll add useful rules as I come across, use, and test them.
Add www Prefix
This rule adds the “www” prefix to any URL, which is a common SEO requirement.
<rule name="non www to www" enabled="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" negate="true" pattern="^www\.([.a-zA-Z0-9]+)$" />
</conditions>
<action type="Redirect" url="http://www.{HTTP_HOST}/{R:0}" appendQueryString="true" redirectType="Permanent" />
</rule>
Remove www Prefix
Want the reverse option? Sure, use this rule to remove the www prefix:
<rule name="Remove www" stopProcessing="true">
<match url="(.*)" ignoreCase="true" />
<conditions logicalGrouping="MatchAll">
<add input="{HTTP_HOST}" pattern="^www\.(.+)$" />
</conditions>
<action type="Redirect" url="http://{C:1}/{R:0}" appendQueryString="true" redirectType="Permanent" />
</rule>
Remove www Prefix (for a site that uses both http and https)
The previous rule for this isn’t enough to work for both http and https resources in the same rule. Here’s two rules that will cover this – in theory you should be able to do this with a single rule, but two feels more specific to a casual reader.
Thanks to @rinidpp for these two rules.
<rule name="Remove www http" stopProcessing="true">
<match url="(.*)" ignoreCase="true" />
<conditions logicalGrouping="MatchAny" trackAllCaptures="true">
<add input="{HTTP_HOST}" pattern="^www\.(.+)$" />
<add input="{HTTP}" pattern="^http\:\/\/$" />
</conditions>
<action type="Redirect" url="http://{C:1}/{R:0}" appendQueryString="true" redirectType="Permanent" />
</rule>
<rule name="Remove www https" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAny" trackAllCaptures="true">
<add input="{HTTP_HOST}" pattern="^www\.(.+)$" />
<add input="{HTTPS}" pattern="^https\:|/\/$" />
</conditions>
<action type="Redirect" url="https://{C:1}{R:0}" />
</rule>
Redirect from Domain1 to Domain2
Most useful if you’ve changed your site name, or want to catch an alias and send it through to your main site. If your old/new URLs share common elements then you could adapt this so that the matching pattern and the redirect target was smarter rather than having it hardcoded – this is the simple no fuss version.
<rule name="Domains, there can be only ONE" enabled="true">
<match url="(.*)" ignoreCase="true" />
<conditions>
<add input="{HTTP_HOST}" pattern="myoldsite.com" />
</conditions>
<action type="Redirect" url="http://www.mynewsite.com/{R:0}" appendQueryString="true" redirectType="Permanent" />
</rule>
Simple Content Redirection/Mapping
Useful for when you’ve reorganised a site, and want to ensure that old/removed URLs go through to a replacement page. In this example, a load of old pages are now sent through to the ‘services’ page.
<rule name="toservices" stopProcessing="true">
<match url="^(analysis-design|custom-development|webhosting|websites|video).*" ignoreCase="true"/>
<action type="Redirect" url="/services/" redirectType="Permanent" appendQueryString="true" />
</rule>
Redirection Based on a Specific Subdomain
Match a subdomain and redirect it somewhere else – we used this to remove a deprecated blog site. Could easily be adapted to send blog.mysite.com/someentry/ through to mysite.com/blog/entry/ if required (i.e. moving your blog off a subdomain for SEO reasons).
In the case described above, you’ll also want to add the hostname of the old blog domain to the destination site’s bindings.
<rule name="Remove Blog" stopProcessing="true" enabled="true">
<match url="(.*)" ignoreCase="true" />
<conditions>
<add input="{HTTP_HOST}" pattern="^(blog\.)(.*)$" />
</conditions>
<action type="Redirect" url="http://www.replacementsite.com/" redirectType="Permanent" />
</rule>
Content Redirection, Conditional on Domain Name
This is similar to the case above, but used for a slightly different case. We had one site bound to multiple domain names, and wanted to use a “preview” URL to test product functionality, while leaving it disabled for anyone accessing via livesite.com. So anyone who doesn’t have the secret preview site domain can’t hit the product URLs.
<rule name="conditional products redirect" enabled="true">
<match url="^products(.*)" ignoreCase="true" />
<conditions>
<add input="{HTTP_HOST}" pattern="www.livesite.com" />
</conditions>
<action type="Redirect" url="/coming-soon-page/" appendQueryString="false" redirectType="Found" />
</rule>
Domain Conditional Sitemaps
This caters for an instance where you have a single site which serves multiple domain names, and you want to have a specific Sitemap served up per domain. Note that this is not a redirect, it’s a rewrite. The example matches based on NZ/AU specific domain suffixes, and can be altered as per your requirements. This also works nicely for domain specific robots.txt files.
<rule name="sitemapnz" enabled="true">
<match url="^sitemap.xml" ignoreCase="true"/>
<conditions>
<add input="{HTTP_HOST}" pattern="\.co\.nz"/>
</conditions>
<action type="Rewrite" url="/sitemapnz.xml"/>
</rule>
<rule name="sitemapau" enabled="true">
<match url="^sitemap.xml" ignoreCase="true"/>
<conditions>
<add input="{HTTP_HOST}" pattern="\.com\.au"/>
</conditions>
<action type="Rewrite" url="/sitemapau.xml"/>
</rule>
Remove Default.aspx
For some ASP.NET sites, /Default.aspx is synonymous with “/”. This is a nice simple way of making your URLs tidier, mostly for visual purposes.
<rule name="Default Document" stopProcessing="true">
<match url="(.*?)/?Default\.aspx$" ignoreCase="true"/>
<action type="Redirect" url="{R:1}"/>
</rule>
HTTP/HTTPS Redirection
This is probably one of the best reasons to use UrlRewrite – sending users between HTTP/HTTPS can often lead to horrible conditional statements in your code checking for dev/test mode. This allows a much tidier way to handle it without the need for such statements.
Obviously this one has a pair of rules, one for each way. Both rules have a check as to whether the protocol in use is http/https. Both rules are working on the same list of “pages” (URL patterns) to match, however the redirect to HTTP is not matching those pages (i.e. it’s the reverse of the first), and has a couple of .NET/site specific paths for exclusion.
<rule name="Redirect to HTTPS" stopProcessing="true">
<match url="^(myaccount|checkout|login|logout|register|forgotpassword|changepassword).*" ignoreCase="true" />
<conditions>
<add input="{HTTPS}" pattern="^OFF$" />
<add input="{HTTP_HOST}" pattern="^([\d\w\.]+)" />
</conditions>
<action type="Redirect" url="https://{C:1}{REQUEST_URI}" redirectType="SeeOther" appendQueryString="false" />
</rule>
<rule name="Redirect to HTTP" stopProcessing="true">
<match url="^(?!webresource|_assets|handlers|myaccount|checkout|login|logout|register|forgotpassword|changepassword).*" ignoreCase="true" />
<conditions>
<add input="{HTTPS}" pattern="^ON$" />
<add input="{HTTP_HOST}" pattern="^([\d\w\.]+)" />
</conditions>
<action type="Redirect" url="http://{C:1}{REQUEST_URI}" redirectType="SeeOther" appendQueryString="false" />
</rule>
Rewrite all HTML files, except Umbraco
We're getting into rules for slightly more obscure requirements right now. This one is for a site where the customer wanted to explicitly redirect all HTML files to a specific 'not found page' (they'd just migrated over from a static site), however for this to work it needed to exclude the Umbraco directory which serves up some HTML files as part of its back office.
<rule name="rewritehtmlfilesexceptUmbraco" stopProcessing="true">
<match url=".*\.(html)$"/>
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_URI}" negate="true" pattern="^/Umbraco" ignoreCase="true" />
</conditions>
<action type="Redirect" url="/page-not-found/" redirectType="Permanent" appendQueryString="true" />
<add input="{REQUEST_URI}" negate="true" pattern="^/App_Plugins" ignoreCase="true" />
</rule>
Create an alias to a MailChimp Campaign
This rule enables you to use a ‘tidy’ link on a site as an alias to an external MailChimp (or any other) campaign. The one thing to note is that any ampersand characters that are present in the URL must be encoded, which you can see below as ‘&’ in the redirect URL.
<rule name="External Mailchimp" stopProcessing="true">
<match url="^newsletter" ignoreCase="true" />
<action type="Redirect" url="http://yourmailchimpurl.us2.list-manage.com/subscribe/post?u=c550d6beeb111121b36303115&id=89602d8f58" redirectType="SeeOther" appendQueryString="true" />
</rule>
Other useful links
Link: URL Rewrite Module Configuration Reference
Link: Ourace IIS rewrite rules – contains a number of other SEO ones which I’d not used before, but look useful. Specifically removing the trailing slash, converting all URLs to lowercase, and returning the 503 Status Code.
Tags: IIS
Posted on Tuesday, January 14, 2014 9:19 PM |