ASP.NET 404 Custom Errors & IIS

Posted by Matthew Osborn on May 27, 2011

Recently I have been spending a fair amount of time working on the NuGet Documentation site. One of the improvements I wanted to make to it was to add useful error pages. You know more than the YSOD (yellow screen of death). One of the major issues for me was creating a useful and informative 404 page. I wanted the page to tell the user why they got there, offer suggestions about what page they may be looking for, and allow them to search the site. So I did the development work committed the changes and had the CI machine push to the server (in the case app harbor).

  1. <system.web>
  2.     <compilationdebug="true"targetFramework="4.0" />
  3.     <customErrorsmode="On"defaultRedirect="~/error">
  4.         <errorstatusCode="404"redirect="~/404" />
  5.     </customErrors>
  6. </system.web>

But I was still seeing the generic IIS sever errors! I did some searching on the internet and Twitter and found a helpful property of the response object.

  1. Response.TrySkipIisCustomErrors = true;

By default IIS will see an response with an error status code (400-500) and will automatically change the content of the response to its default error page. What this property does it tell IIS not to do that if there is already content in the body of the page. Well that was easy, right? So I made the change and pushed it to the server, and navigated to a page that didn’t exist. Well the server still returned me the default 404 page, but on a 500 it would give me my custom error page. So what gives? Well here is the deal, the way routing works is that if ASP.NET cannot find a file that matches the requested URL the request is given back to IIS to handle. So in the case of a 404 ASP.NET can’t find the file so its given back to IIS who uses the default static file handler to serve the request. This would be slightly different if the route had matched but then say somewhere in our code we set the status to 404. In this case it would already be in the ASP.NET pipeline and ASP.NET would server the custom 404 page.

There are two ways to solve this problem. First is the easiest which is to open up IIS Manger and go to the “Error Pages” settings under IIS and change the 404 page there to use your custom 404 page.

IIS

This is fine if you can remote into the server or you’re not running in the cloud where multiple instances can be started. So how then do you make that work? Well lucky for us starting with IIS7 and later these settings can be added to your web.config file under the System.WebServer node.

  1. <system.webServer>
  2.     <httpErrorserrorMode="Custom" >
  3.         <removestatusCode="404"subStatusCode="-1"/>
  4.         <errorstatusCode="404"path="/404"responseMode="ExecuteURL" />
  5.     </httpErrors>
  6. </system.webServer>

So lets dig into what some of this code means and tell you about the tricky parts that you need to know. Before you can add a custom page you need to remove the entry for the status code as there can only be one entry per status code. The tricky bit here is knowing to set SubStatusCode to –1. This basically means all the possible sub statuses. If you like you could remove only the specific one you needed and set only the specific one. Also if you are playing around with the config you might find that there is a defaultPath attribute on the httpErrors node. This defines a “default” error page should an entry not be found in the list. The problem is that by default this is “locked” and cannot be set in an applications web.config and instead needs to be set at the machine level.  Once you add these settings to your config you should be able to see your custom error page when you navigate to a page that does not exist. As a side note this just drives home the importance of IIS Express and why you should use it for development. This would have been discovered prior to pushing changes to the live server that way.