Mapping a different file extension for ASPX Pages in IIS 7.0

Today I read a question in one of the IIS.NET forums - although I'm not sure if this is what they really wanted to know - I figured it might be useful to understand how to do this anyway. Several times users does not like exposing their ASP.NET pages using the default .aspx file extension (sometimes because of legacy reasons, where they try to minimize the risk of generating broken links when moving from a different technology, to preserve the validity of previous search-engines-indexes and sometimes for the false sense of security or whatever).

Regardless of why, the bottom line, to map a different file extension so they behave just like any other ASP.NET page requires you to add a couple of entries in configuration, especially if you want those to be able to work in both Pipeline Modes "Classic and Integrated".

For this exercise lets assume you want to assign the file extension .IIS so that they get processed as ASPX pages and that you only want this to be applicable for Default Web Site and its applications.

The following Web.Config file will enable this to work for both Pipeline Modes:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <add name="ASPNETLikeHandler-Classic" path="*.iis" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" />
<add name="ASPNETLikeHandler" path="*.iis" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" modules="ManagedPipelineHandler" requireAccess="Script" preCondition="integratedMode" />
</handlers>
        <validation validateIntegratedModeConfiguration="false" />
</system.webServer>
    <system.web>
        <compilation>
            <buildProviders>
                <add extension=".iis" type="System.Web.Compilation.PageBuildProvider" />
</buildProviders>
        </compilation>
        <httpHandlers>
            <add path="*.iis" type="System.Web.UI.PageHandlerFactory" verb="*" />
</httpHandlers>
    </system.web>
</configuration>

The following command lines uses AppCmd.exe to enable this for both Pipeline Modes and basically generate the .config file above.

echo To make it work in integrated mode
appcmd.exe set config "Default Web Site" -section:system.webServer/handlers /+"[name='ASPNETLikeHandler',path=' *.iis',verb='GET,HEAD,POST,DEBUG',type='System.Web.UI.PageHandlerFactory',modules='ManagedPipelineHandler',requireAccess='Script',preCondition='integratedMode']"
appcmd.exe set config "Default Web Site" -section:system.web/compilation /+"buildProviders.[extension=' .iis',type='System.Web.Compilation.PageBuildProvider']"

echo To make it work in classic mode
appcmd.exe set config "Default Web Site" -section:system.webServer/handlers /+"[name='ASPNETLikeHandler-Classic',path=' *.iis',verb='GET,HEAD,POST,DEBUG',modules='IsapiModule',scriptProcessor='%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll',requireAccess='Script',preCondition='classicMode,runtimeVersionv2.0,bitness32']"

appcmd.exe set config "Default Web Site" -section:system.web/httpHandlers /+"[path=' *.iis',type='System.Web.UI.PageHandlerFactory',verb='*']"

appcmd.exe set config "Default Web Site" -section:system.web/compilation /+"buildProviders.[extension=' .iis',type='System.Web.Compilation.PageBuildProvider']"

appcmd.exe set config "Default Web Site" -section:system.webServer/validation /validateIntegratedModeConfiguration:"False"

So what does this actually do?

Lets actually describe the AppCmd.exe lines since it breaks nicely the different operations.

  1. Integrated Mode. When running in Integrated mode conceptually only the IIS Pipeline gets executed and not the ASP.NET pipeline, this means that the HttpHandlers section (in system.web) is actually not used at all.
    1. So just by adding a new handler (ASPNETLikeHandler above) to the System.WebServer/Handlers will cause IIS to see this extension and correctly execute the page. Note that we use the preCondition attribute to tell IIS that this handler should only be used when we are running in an Integrated Pipeline Application Pool.
    2. The second line only tells the ASP.NET compilation infrastructure how to deal with files with this extension so that it can compile it as needed.
  2. Classic Mode. In classic mode the ASP.NET pipeline keeps running as previous versions of IIS as a simple ISAPI and the new IIS pipeline gets executed as well, this means that we first need to tell IIS to route the request to ASP.NET ISAPI and then we need to tell ASP.NET how to handle this extension as well by using the system.web/httpHandlers to process the request.
    1. The first line adds a handler (ASPNETLikeHandler-Classic above) to IIS so that IIS correctly routes this request to the aspnet_isapi.dll. Note that in this case we also use the preCondition attribute to tell IIS that this only applies when running in an Application Pool in classic mode, furthermore we also use it to specify that it should only be used in an Application Pool running in 32 bit mode and that is using version 2.0 of the runtime. If you wanted to support also 64 bit application pools you would add another handler pointing to the 64bit version of aspnet_isapi.dll in Framework64 folder and use bitness64 instead.
    2. The second line tells ASP.NET that the .iis extension is handled by the PageHandlerFactory, so that the ASP.NET pipeline understands what to do when a file with the .iis extension is requested.
    3. Again, just as 2 in the Integrated Mode case we need to tell the ASP.NET compilation infrastructure how to deal with this extension.
    4. Finally since we are adding a system.web/httpHandler entry and to make sure that this will not break when someone changes the App pool to integrated mode we tell IIS to "not complain" if we get run in Integrated mode since we also included the handler in the IIS sections in the right way.

Hopefully this helps understanding a bit how to re-map extensions to ASP.NET extensions, and in doing that learn a bit more about preConditions, Handlers and AppCmd.