Scott Hanselman

Redirecting ASP.NET Legacy URLs to Extensionless with the IIS Rewrite Module

May 11, 2013 Comment on this post [19] Posted in ASP.NET | IIS
Sponsored By

ASP.NET has included support for "friendly URLs" for a while now. ASP.NET MVC has always supported friendly URLs and more recently, so has Web Forms. That means if you don't want to have the .aspx extension, you certainly don't have to.

However, there's a LOT of existing legacy apps out there as well as apps that you may not have full control over. For example, there's a site that I want to influence but it's got dozens (hundreds) of links to foo.html and bar.html existing pages.

Legacy (n): A super-fancy way of saying "already exists."

What I want to do is kind of sloppy and I'm doing it for aesthetic reasons. I'll hopefully get around to updating the site's links later and know that future links will be extensionless. But look at me, I'm justifying why I'm doing this, Dear Reader. You of all people know that sometimes you just gotta do something just because ya gotta Get It Done™©.

I want to:

  • redirect existing GETs to a /foo.html to /foo
    • Redirects are external
  • but, keep rewriting /foo to the underlying /foo.html so it handles the request
    • Rewrites are internal

I can do all this within my web.config using the IIS Url Rewrite Module.  I can do this with ANY file type that IIS can handle, meaning this isn't an ASP.NET-specific thing. This all happens well before your application gets involved. You'll note I did a similar thing with a PHP app running under IIS just last month.

Here's what my web.config looks like. Note that since I have Azure (or in just IIS 7+ and the Rewrite module) I just added this file. There was no configuration needed. The same would apply to any existing site. Be aware that sometimes super-"greedy" rewrite or redirect rules can end up grabbing ahold of your CSS or JS so you'll want to be aware if something odd happens.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="extensionless" stopProcessing="true">
<match url="(.*)\.html$" />
<action type="Redirect" url="{R:1}" redirectType="Permanent" />
</rule>
<rule name="removeextension" enabled="true">
<match url=".*" negate="false" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{URL}" pattern="(.*)\.(.*)" negate="true" />
</conditions>
<action type="Rewrite" url="{R:0}.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

This stuff is hard to write, though.

SIDE NOTE: My RegEx expert Ruslan points out that the final Action could be simplified slightly like this:

<add input="{URL}" pattern="\." negate="true" />

This stuff is also hard to test. The IIS Rewrite module has a great UI for IIS that will write a lot of these rules for you and let you test them interactively:

URL Rewrite module in IIS

Hope this helps. I wrote this post (and bookmarked it) for myself because I am always googling around for this particular rule to remind myself. Now I'll search my own blog. ;) Reason #64 to blog, friends.


SPONSOR: Big thanks to the feed sponsor this past week, Ext.NET (seriously, check out their demos, really amazing stuff!) - Quickly build modern WebForm and MVC (including RAZOR) Apps for ASP.NET. Free pancake breakfast with all purchases!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
May 11, 2013 3:16
From my experience:

Legacy(n): An educated way of saying: Cr*** that we didn't write, is very bad code, are not responsable for and want out the door asap, but the manager doesn't let us rewrite it.

May 11, 2013 3:27
I was seriously just looking for this! Thanks for all the work you put into this blog. I hope you know your it's appreciated!
May 11, 2013 3:52
Great post!
May 11, 2013 6:01
What are the SEO implications of such a rewrite? Does the redirect respond with the correct headers for a permanent redirect?
May 11, 2013 8:26
Scott - it's the HTTP response code that used used. So 301 Permanent rather than 302 Object Moved.

This line -> action type="Redirect" url="{R:1}" redirectType="Permanent"
May 11, 2013 15:37
One thing - that IIS Url Rewrite editor is very nice. But, these days, few of my sites run locally on IIS, most now use IIS Express. That means I cannot use this editor directly. And Visual Studio doesn't even have Intellisense for URL Rewrite. Currently, I have a dummy site in IIS that I use to set up rules and then I copy the XML over (or I try to write it by hand, if I can remember the correct syntax). Surely there has to be a better way?
May 11, 2013 16:02
Are there solutions for those of us still supporting IIS 6?
May 11, 2013 23:52
Maybe it's just me, but your rules seemed to be named backwards. I would expect the "extensionless" rule to do the rewrite to the file with the extension and the "removeextension" rule to, well, remove the extension and do a redirect.
May 12, 2013 15:38
Another useful tool when debugging rewrites is the failed request tracing in IIS. You can see each rewrite rule as it processed. Very useful.
May 12, 2013 18:59
Nick, if you're using II6 then check out Ionic's IIRF ( http://www.codeplex.com/IIRF ). I wrote an article a few years ago with a working example of IIRF in action... http://swortham.blogspot.com/2008/12/redirecting-default-page-defaultaspx-to.html
May 13, 2013 17:41
Your blog is never fit for a mobile device. any code section added always gets cut. hope u will do sumthing about it soon.
May 13, 2013 18:10
Naveed - it seems to be fine on my IE on WP8; the code section does cut off on the right, but I can scroll inside the code section as needed. YMMV, of course.
Joe
May 13, 2013 20:44
Naveed and Joe - Yes, I designed this with @jzy to absolutely look great on mobile. You can touch scroll horizontally within code blocks.
May 13, 2013 20:55
I was initially using my Nokia E-500 and now I am using Samsung Galaxy Y (Android) mobile. Same result on both the code sections are always cut and I can not scroll horizontally on my mobile device...
May 14, 2013 22:50
Great post and it works great on my Samsung Galaxy Nexus running Android 4.2.2, code scrolling and all.
May 15, 2013 0:34
Thanks for this article, good knowledge. I hope the irony isn't lost on you that this post is delivered with an "aspx" extension! .LOLz!
May 15, 2013 2:41
.LAG - It's not. But this site is under .NET 2.0 and it's worked great for years. I don't want to change it now.
May 24, 2013 1:47
Nice Post, Scott. I have one question, if you type foo1 or foo1.html and the file does not exist I get a 500 Internal server error, instead of 404. Any ideas how to prevent that.
August 08, 2013 23:37
Legacy (n): A super-fancy way of saying "currently works"...

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.