IIS7 Http Errors Configuration on Lockdown

1/22/2009 8:16:42 PM

Technologies

  • ASP.NET MVC
  • IIS 7
  • IIS 7 Administration

My general rule of thumb is that when I run into a problem once I fix it. When I run into it twice I try and automate it. I've now had the default http errors setting for IIS7 burn me four times on four different projects. I figure it's about time to automate it (because I keep forgetting how to solve it) and as penance blog about the issue and solution.

The default IIS7 <httpErrors /> setting for a website is to show Detailed errors for local requests and custom error pages for remote requests. So when an http error such as 500 occurs during a remote request IIS7 intercepts the returned page and instead displays the custom error page that it has set.

default http errors setting for iis7

Most websites I work on tend to need more robust error handling then just returning the default custom error pages set in IIS7. Specifically when working with ASP.NET MVC you often want to return your own view when an error occurs. You may even be using the ASP.NET MVC [HandleError] attribute which is affected by this as well.

All of your custom error rendering or usage of [HandleError] works flawlessly when developing locally since by default IIS7 returns the detailed error page. Roughly speaking when returning the detailed error page the web.config <customErrors /> determines what gets displayed. If you have <customErrors mode="On" /> then your error views are rendered. If you have <customErrors mode="Off" /> then a stackdump gets displayed.

The first problem arrises when you deploy your application and test out your error views. You will see the following not so nice error page instead of the one you have been seeing during development.

iis7 custom error page looks bad

Since you are no longer hitting the site locally IIS7 intercepts your nice error view and returns the default one. In order to display yours for remote requests <httpErrors errorMode="Detailed" /> must be set. This brings us to the next problem.

   1:  <system.webServer>
   2:      <httpErrors errorMode="Detailed" />
   3:  </system.webServer>

<httpErrors /> Lockdown

The <httpErrors /> node is locked down in the IIS7 config system. and when you try to set it in your web.config you will end up seeing errors like this one:

The IIS7 config system is really hard to undertstand and is spread out all over the place. Maybe one day I'll rant about this in more detail...

This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault="Deny"), or set explicitly by a location tag with overrideMode="Deny" or the legacy allowOverride="false".

In order to set <httpErrors /> you have to open up applicationHost.config on your server and unlock the node or specifically set the value for the site you want. I like to unlock the node and set <httpErrors /> within the web.config of my application. I find this much clearer and more isolated then having application specific settings defined within the applicationHost.config on a specific server.

Programmatically Unlock <httpErrors /> So I Never Have To Remember How Ever Again

I can never remember where IIS7 stores all of it's config files and everytime I have to go make this change I either end up searching all over the place to find the applicationHost.config or use the IIS7 Manager to configure each site one by one. It was time for a little application that can do the dirty work for me.

One of the great things about IIS7 is that it has a managed API (Microsoft.Web.Administration) that can be used to program it. Although you must be an expert code spelunker in order to use the API it is very usefull and the little bit of code below can be used to unlock the <httpErrors /> node for all the sites on your server.

   1:  static void Main(string[] args)
   2:  {
   3:      string server = "localhost";
   4:      if (args.Length == 1)
   5:          server = args[0];
   6:   
   7:      ServerManager manager = ServerManager.OpenRemote(server);
   8:   
   9:      Configuration config = manager.GetApplicationHostConfiguration();
  10:      ConfigurationSection section = config.GetSection("system.webServer/httpErrors");
  11:      section.OverrideMode = OverrideMode.Allow;
  12:   
  13:      manager.CommitChanges();
  14:  }

If you build that sucker and run it all of your sites can set <httpErrors errorMode="Detailed" /> in their web.config and all your pretty error pages can get displayed for remote requests.

Comments

Shailesh Patel

Shailesh Patel

http://PatelShailesh.com
3/4/2010 11:58:16 AM

I am looking for the code to update httperrors for website level not a webserver level. Do you have any idea how I can do that? I have done that in IIS6 since we are moving to IIS7 I am looking for the code to update HTTPErrors for website level programattically. Have any idea about that?