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" />
</customErrors>

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.Server.ClearError();
    application.Context.Handler = 
          System.Web.UI.PageParser.GetCompiledPageInstance(fourOhFourPage, 
          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"  
       redirectMode="ResponseRewrite">
  <error statusCode="404" redirect="~/Errors/Error404.aspx" />
  <error statusCode="500" redirect="~/Errors/Error500.aspx" />
</customErrors>

Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Thursday, August 06, 2009 8:23:07 PM pthalacker said:
I am not seeing the same result that you show.  It successfully transfers to the error page without registering the redirected page. When I use the code as written in your Application_Error example, DoesNotExist.aspx returns a status of 200, not 404.

pamela
On Thursday, August 06, 2009 8:26:44 PM Imar Spaanjaars said:
Hi pthalacker,

Do you have the following code in the error page?

Response.Status = "404 Not Found";
Response.StatusCode = 404;

You need that, or you still get a 200 status.

Cheers,

Imar
On Monday, May 13, 2013 9:38:04 PM elnath78 said:
ive the following web.config but i still get a 404 a real 404 erro i mean, with rewrite it doesnt works, only redirect but url looks ugly


[customErrors defaultRedirect="~/index.aspx" redirectMode="ResponseRewrite" mode="On"]

[error statusCode="404" redirect="~/handler.ashx" /]

[/customErrors]
On Monday, May 13, 2013 11:34:54 PM elnath78 said:
Update: i noticed the issue is the .ashx if i use a normal .aspx it works... so why i cant use .ashx files with ResponseRewrite? it is just their purpose as file dedicated to handle errors..
On Tuesday, May 14, 2013 6:47:39 AM Imar Spaanjaars said:
Hi elnath78,

I can reproduce the problem, but I don't know the fix. It seems it has to do with how how 404s and handlers are processed internally but I am not sure.

Sorry.

Cheers,

Imar
On Tuesday, May 14, 2013 8:41:13 AM elnath78 said:
hello, yes for some reason seems like that, im unable to use httperrors but i wonder if it gives the same result of differently would works
On Tuesday, July 16, 2013 11:17:31 AM ps said:
I've tried the above code, but its giving 404 error without redirecting to error page.
On Sunday, July 28, 2013 7:17:37 PM Imar Spaanjaars said:
Hi ps,

Then maybe your 404 doesn't exist or results in an error?

Imar

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 QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.