Proper Handling of 404 Errors Using redirectMode

ASP.NET has a handy feature that lets you easily determine an error page that gets displayed in case an error occurs on your server. Using the customErrors section in the web.config file you can assign specific pages that are displayed for specific HTTP error codes. The following example shows how 404 errors (page not found) and 500 errors (server errors) are routed to the file ~/Errors/Error404.aspx and ~/Errors/Error500.aspx respectively. All other errors are handled by the generic ~/Errors/Error.aspx page:

<customErrors mode="On" defaultRedirect="~/Errors/Error.aspx">
  <error statusCode="404" redirect="~/Errors/Error404.aspx" />
  <error statusCode="500" redirect="~/Errors/Error500.aspx" />

Although easy to use and set up, this solution has some drawbacks that might impact how search engines see your site and how 404 errors are treated. The good news is: these problems can easily be overcome.

Using Custom Error Pages

The problem is that ASP.NET doesn't change the status code to 404 when a page cannot be found. For example, with the configuration section shown earlier, a request for a non-existing page like DoesNotExist.aspx generates the following response in Fiddler:

Output of Fiddler when no extra precautions are taken
Figure 1

Although the page DoesNotExist.aspx does not exist, the browser (or search agent) doesn't receive a 404 error code, but a 302 error code which tells it to look elsewhere for the requested page. The browser happily accepts the instructions and then requests the page /Errors/Error404.aspx?aspxerrorpath=/DoesNotExist.aspx. This in turn returns a http 200 code (OK) so as far as the browser or search agent are concerned, everything is OK and DoesNotExist.aspx has simply moved.

To overcome this problem you can set the 404 status code in the Page's Load event of the Error404.aspx page like this:

protected void Page_Load(object sender, EventArgs e)
  Response.Status = "404 Not Found";
  Response.StatusCode = 404;

When you now request DoesNotExist.aspx again, Fiddler tells you this:

A Fiddler result with the error page setting the status code to 404
Figure 2

This is hardly any better; the original request still generates a 302 redirect request that the browser or agent happily accepts. However, now the page that is being redirected to results in a 404 error. Not really the result you're after....

Before ASP.NET SP1 one of the solutions to this problem was to intercept the http 404 exception in the Global.asax file and then manually execute the Error404 page by reassigning the current handler using System.Web.UI.PageParser.GetCompiledPageInstance, like this:

protected void Application_Error(object sender, EventArgs e)
  HttpApplication application = (HttpApplication)sender;
  Exception lastError = application.Server.GetLastError();
  HttpException ex = lastError as HttpException;
  string fourOhFourPage = "~/Errors/Error404.aspx";
  // Only response to 404 errors
  if (ex != null && ex.GetHttpCode() == 404)
    // Clear the error in order to avoid standard handling of the error.
    application.Context.Handler = 
          application.Server.MapPath(fourOhFourPage), application.Context);

Now when you request DoesNotExist.aspx you should see this:

Fiddler showing a correct 404 response
Figure 3

Nice. But it requires quite a bit of work, and it no longer uses the <customErrors /> section, instead hard coding the Erorr404.aspx page in the Global.asax file.

With ASP.NET 3.5 SP1 Microsoft introduced a new attribute on the <customErrors /> section for the web.config file called redirectMode. Its default value is ResponseRedirect which results in the same behavior as before. However, the setting ResponseRewrite results in the Error404.aspx page being executed and sent to the output using a rewrite, rather than a redirect. This means the address bar in the browser stays the same, and the requested page (which wasn't found) nicely results in a 404 error. With the custom code removed from the Global.asax file, and the web.config modified to read like below, requesting a non-existent page now results in the exact same behavior as shown in Figure 3: a nice 404 error code directly on the page a user requested.

<customErrors mode="On" defaultRedirect="~/Errors/Error.aspx"  
  <error statusCode="404" redirect="~/Errors/Error404.aspx" />
  <error statusCode="500" redirect="~/Errors/Error500.aspx" />

Where to Next?

Wonder where to go next? You can post a comment on this article.

Doc ID 497
Full URL
Short cut
Written by Imar Spaanjaars
Date Posted 07/05/2009 15:54


Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the Doc ID of the document.

(Plain text only; no HTML or code that looks like HTML or XML. In other words, don't use < and >. Also no links allowed.