Take the 2-minute tour ×

I have a asp.net mvc application and am trying to get custom errors working with IISExpress.

Works in Casini fine:

<customErrors mode="On" defaultRedirect="/error">
  <error statusCode="404" redirect="/error/notfound"/>

When I've deployed mvc sites to IIS (7.5) before, all I had to do get my custom errors working was to set:

<httpErrors errorMode="Detailed"/>

I've tried explicitly specifying the status codes within the httpErrors section but nothing works. Here's an example:

<httpErrors errorMode="Detailed" defaultResponseMode="Redirect">
  <error statusCode="404" path="/error/notfound"/>

Any ideas?

Thanks Ben

share|improve this question
add comment

2 Answers

up vote 9 down vote accepted

This was caused partly due to my misunderstanding of how custom errors are actually invoked and also the fact that (IMHO), the handling of errors in asp.net mvc is a bit messed up.

The first issue was that in a number of my action methods, I was checking for the existence of an object e.g. a blog post, and returning a HttpNotFoundResult if the blog post was null. I was under the assumption that this would then display the custom error page that I had set up for 404 errors.

However, this is not the case. Returning a HttpNotFoundResult simply sets the status code of the response to 404. The rest is then handled by IIS, displaying the IIS 404 error page or by your browser if it has it's own custom error page.

One solution here is to return a HttpException which will use your custom error pages since the request is be handled by asp.net.

I chose instead to create a new ActionResult that allowed me to specify a view along with a http status code. I preferred this to throwing exceptions.

The next issue was that by default a new MVC project has a greedy route defined. If you make a request to /foo/bar the default MvcHandler will look for a controller called Foo. When it can't find it, it will return 404.

I had removed the default route and had no greedy routes. This meant that urls not matching any of my routes would not be handled by asp.net and would just fall back to IIS.

The solution here was to create a wildcard route at the bottom of my routing configuration to match all other requests and forward them to a custom PageNotFound action, that sets the status code to 404 and displays my custom view.

Some things worth pointing out.

  1. You will need to set httpErrors errorMode="Detailed" for your custom error pages to be displayed in IIS/IISExpress. The rest however can be left alone.
  2. Setting the defaultRedirect path in the customErrors section has no effect on 500 errors. This is because the global HandleErrorAttribute handles all 500 errors and just looks for a view called "Error" to display. This means that if your custom error page is actually a controller action, it will not be invoked. The above is true even if you explicitly specify a 500 error page.
  3. You should still keep the defaultRedirect path however, as it will be used for other status codes if they are not specified explicitly.
share|improve this answer
httpErrors errorMode="Detailed" was missing part for me, thanks –  Sergey Osypchuk Jun 29 '11 at 13:43
add comment

changing only web.config won't work additionaly you change following file also

Detailed error output (errorMode="Detailed") is locked by default, so you need to change this first:

  1. Open %windir%\system32\inetsrv\config\applicationhost.config with a text editor

  2. Find the section named "httpErrors" (ie., <section name="httpErrors" overrideModeDefault="Deny" />) and change overrideModeDefault to "Allow" (ie: <section name="httpErrors" overrideModeDefault="Allow" />)

  3. Find the httpErrors element (ie: <httpErrors>) and unlock ONLY the errorMode by changing that element tag to this:

Now you can use the configuration you posted. Do be aware that this can be a potential security risk, hence it is locked by default. Welcome to the joys of IIS...

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];
 7:      ServerManager manager = ServerManager.OpenRemote(server);
 9:      Configuration config = manager.GetApplicationHostConfiguration();
10:      ConfigurationSection section = config.GetSection("system.webServer/httpErrors");
11:      section.OverrideMode = OverrideMode.Allow;
13:      manager.CommitChanges();
14:  }

If you build that sucker and run it all of your sites can set in their web.config and all your pretty error pages can get displayed for remote requests.

share|improve this answer
add comment

Your Answer


