A lot has been written about how to get ASP.NET MVC running on IIS 6 with extensionless URLs. Up until now, the story hasn’t been very pretty. When running ASP.NET MVC on ASP.NET 4, it gets a lot easier.

To be fair, the part that makes it easier has nothing to do with ASP.NET MVC 3 and everything to do with a little known new feature of ASP.NET 4 creatively called the ASP.NET 4 Extensionless URL feature. ASP.NET MVC 3 requires ASP.NET 4 so it naturally benefits from this new feature.

If you have a server running IIS 6, ASP.NET 4, and ASP.NET MVC 3 (or even ASP.NET MVC 2. I haven’t tried ASP.NET MVC 1.0), your website should just work with the default extensionless URLs generated by ASP.NET MVC applications. No need to configure wildcard mappings nor *.mvc mappings. In fact, you don’t even need to set RAMMFAR to true. RAMMFAR is our pet name for the runAllManagedModulesForAllRequests setting within the system.webserver setting in web.config. You can feel free to set this to false.

<modules runAllManagedModulesForAllRequests="false"/>

When installing ASP.NET 4, this is enabled by default. So if you have a hosting provider still using IIS 6, but does have ASP.NET 4 installed, then this should work for you, unless…

How does this work?

To be honest, it’s a bit of voodoo magic as far as I can tell and there’s a lot of caveats when it comes to using this feature with IIS 6. There are edge cases where it can cause problems. This is why Thomas Marquardt, the implementor of the feature, wrote a blog post on how to disable ASP.NET 4.0 Extensionless URLs just in case.

In that blog post, he describes the bit of magic that makes this work.

Here’s how the v4.0 ASP.NET extensionless URL features works on IIS 6.  We have an ISAPI Filter named aspnet_filter.dll that appends “/eurl.axd/GUID” to extensionless URLs.  This happens early on in the request processing.  We also have a script mapping so that “*.axd” requests are handled by our ISAPI, aspnet_isapi.dll.  When we append “/eurl.axd/GUID” to extensionless URLs, it causes them to be mapped to our aspnet_isapi.dll, as long as the script map exists as expected.  These requests then enter ASP.NET where we remove “/eurl.axd/GUID” from the URL, that is, we restore the original URL.  The restoration of the original URL happens very early.  Now the URL is extensionless again, and if no further changes are made

He also has a list of conditions that must be true for this feature to work. If any one of them is false, then you’re back to the old extensionfull URLs with IIS 6.

I’m Getting a 403 Forbidden

This is not technically related, but when I tried to test this out to confirm the behavior, I ran into a case where every request was giving me a 403 Forbidden error message. Here’s how I fixed it.

In IIS Manager, I right clicked on the Web Services Extension node and selected the menu option labeled Allow all Web Service extensions for a specific application:

iis6-allowing-extensions

In the resulting dialog, I chose the ASP.NET v4.0.30319 option.

iis6-allow-web-service

To double check that everything was configured correctly, I looked at the properties for my website and ensured that Scripts were enabled.

iis6-home-directory-tab

I also clicked on the Configuration… button and made sure that *.axd was mapped to the proper ASP.NET Isapi dll (aspnet_isapi.dll).

iis6-isapi

iis6-extension-mapping

With all that in place, I was able to run standard ASP.NET MVC web application and make requests for /, /home/about/, etc. without any problems!