Setting up ASP.Net MVC 2 on IIS6 with IISRewrite

Setting up ASP.Net MVC 2 on IIS6 with IISRewrite

This will also work in MVC, as in the first one.

It’s a somewhat well-known fact, (by somewhat I mean amongst web devs), that IIS6 is the IE6 of Servers, its slow, dim-witted, annoying, crap and here to stay.

So to make life a little more bearable and to save the rest of the web-society from a life of .net 2 webform’s, bloated view states and ugly URLs here’s a quick “how to”.

This How To Covers:

  • Setting up your Visual Studio Project for ASP .NET MVC2
  • Creating the project
  • Editing the global.asax file
  • Changes Dissected
  • Setting up IIS6 for ASP.Net MVC2
  • Setting up Rewriting (.htaccess file)
  • Why don’t we use Wild Cards

Setting up your Visual Studio Project for ASP .NET MVC2

Creating the project

This code should fit quite nicely into your code whether your using IOC ala Ninject or already have a load of stuff in your global.asax.cs file.
Anyhoo onwards; go and create a new project; select new MVC2 Web Application; I like to keep all of the junk in, it lets me exercise the delete button once I know the servers behaving.

That’s pretty much, that tiny step, done with.

Editing the global.asax file

Pop open your global.asax.cs file in Visual Studio and look for public class MvcApplication(){ shouldn’t be too hard to see there should be very little in there.

At this point, you can see (as above) right at the start of public class MvcApplication { our magic will happen… copy and paste the following code there.

#region 301Redirect Method

/// <summary>
/// Redirect old URL to new URL via 301 Header Redirect (SEO Friendly Permanant Redirect)
/// </summary>
/// <param name="url"></param>
private void PermanentRedirect(string url)
    Response.Status = "301 Moved Permanently";
    Response.AddHeader("Location", url);


#region MVC Request Cleaner

/// <summary>
/// Cleans up passed urls to reduce the number of alternate routes to the same content
/// Strips postfix slash; etc, as in the comments.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Application_BeginRequest(Object sender, EventArgs e)
    #region Rewriting IIS6 Fix 27112009

    var app = sender as HttpApplication;
    if (app != null)
        if (app.Request.AppRelativeCurrentExecutionFilePath == "~/rewritten.aspx")
                app.Request.Url.PathAndQuery.Replace("/rewritten.aspx", "")

    #endregion Rewriting IIS6 Fix 27112009

    // Get the requested URL so we can do some validation on it.
    // We exclude the query string, and add that later, so it's not included
    // in the validation
    string url = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority +

    // If we're not a request for the root, and end with a slash, strip it off
    if (HttpContext.Current.Request.Url.AbsolutePath != "/" &&
        PermanentRedirect(url.Substring(0, url.Length - 1) + HttpContext.Current.Request.Url.Query);

    // If we end with /1 we're a page 1, and don't need (shouldn't have) the page number
    if (HttpContext.Current.Request.Url.AbsolutePath.EndsWith("/1"))
        PermanentRedirect(url.Substring(0, url.Length - 2) + HttpContext.Current.Request.Url.Query);

    // If we have double-slashes, strip them out
    else if (HttpContext.Current.Request.Url.AbsolutePath.Contains("//"))

        PermanentRedirect(url.Replace("//", "/") + HttpContext.Current.Request.Url.Query);

    // If we've got uppercase characters, fix
    else if (Regex.IsMatch(url, @"[A-Z]"))
        PermanentRedirect(url.ToLower() + HttpContext.Current.Request.Url.Query);


I’ve left in the regions so this can be nicely shrunken down rather than making your global asax file look like a garbage dump.

Compiling now should run just fine and you should be able to test out the edits by attempting to load /Home/About/ (should go to lowercase and strip off the last slash)

Dissecting the Code

Firstly we have the ‘PermanentRedirect’ method which I won't go into much detail explaining as the codes not changed much since classic-asp, but basically it sets an SEO friendly ‘HTTP/1.1 301 Moved Permanently Location: http://etc’ on the URL to the rewritten URL. This method's simply here for to facilitate the rest of the code.
Next we have Application_BeginRequest where we pick up the HttpContext request and make our alterations.
Firstly we have our IIS6 fix; which I’ll delve into the “why” in a moment; but simply this takes the request, grabs the path and query and strips out the ‘/rewritten.aspx’ bit. Not necessary in IIS7/8 but it sure as shit beats App-WildCard in IIS6.
The codes pretty well commented but let's expand on it a bit.

string url = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority +                    HttpContext.Current.Request.Url.AbsolutePath);

We capture the URL in its component parts, avoiding the Query String as we really don’t want to transform anything being carried in there.

string url = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority +

 Request.Url.Scheme // this bit will return http or https, basically the bit before ://
 HttpContext.Current.Request.Url.Authority // this will return the DNS name or IP address
 HttpContext.Current.Request.Url.AbsolutePath // returns the rest of the path

Or for extra imagery…

The first if statement checks for slashes, if its the root we leave it alone if it's not the route we make sure the URL doesn’t end in a slash. This may not seem like a major thing but if you have and somewhere else you accidentally link to, although it may not throw an error or cause you any “major” issues, Google will count that as 2 unique URL’s serving identical content which is crap for SEO.
Next, we check for page 1, this usually occurs in results pages but the scenario is the same as above and as your default route for a results page would be the first page it's unnecessary.
Almost at the end; we then check that were not accidentally passing double slashes, this is usually a user error but when you consider that anyone could link to your site its good to cover your bases.
And last but not least, converting to lowercase, a quick regex check for uppercase characters and if they exist convert the URL to lowercase. I’ve not sure if this would benefit from the compiled regex flag as its a bit of a rabbit hole and so far I’ve not seen any reason to use it. If I get around to it I’ll do a quick 10K run test and see if there’s a big improvement vs. offset. Probably won't be as this is a REALLY basic regex test :)

That’s pretty much all of that bit.

Setting up Rewriting (.htaccess file)

I’ll cut to the chase here’s the .htaccess file for your site; your rules may vary
slightly if your using IIRF instead of ISAPIRewrite. I prefer IIRF in general, but work seems to prefer ISAPIRewrite, let's not assume they’re right though as they just took the advice of a consultant.

# Helicon ISAPI_Rewrite configuration file
# Version

RewriteEngine on

# Add any standard 301 Redirections between here
# Dont Add any standard 301 Redirections after here
# MVC + Standard Rules
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# Add extensions to this rule to avoid them being processed by ASP.NET
RewriteRule (.*).(css|gif|png|jpeg|jpg|js|zip) $1.$2 [I,L]

# Prefixes URLs with "rewritten.aspx/", so that ASP.NET handles them
RewriteRule ^(.*) /rewritten.aspx/$1 [I]

Now you can see where the rewritten.aspx comes in, using a file-type filename causes IIS to pass the file through the handler, without having to resort to the wild card method.

Why don’t we use WildCards or a .mvc extension

In short overhead and vanity; wildcarding will cause EVERYTHING to go through ASP.Net (aspnet_isapi.dll) including your static file requests, and adding an extension to the end of the file.
a) Exposes the technology pointlessly,
b) makes ugly URLs
c) takes away from why you're doing this in the first place.


This systems a bit of an amalgamation of fixes and optimizations but the system to get around the asp wildcard came from Mr Sanderson God knows where the other bits came from.

Chris McKee

Chris McKee

Software Engineer, Web Front/Backend/Architecture; all-round tech obsessed geek. I hate unnecessary optimism