100

Having a weird problem here. Everybody knows that if you use web.config's customErrors section to make a custom error page, that you should set your Response.StatusCode to whatever is appropriate. For example, if I make a custom 404 page and name it 404.aspx, I could put <% Response.StatusCode = 404 %> in the contents in order to make it have a true 404 status header.

Follow me so far? Good. Now try to do this on IIS7. I cannot get it to work, period. If Response.StatusCode is set in the custom error page, IIS7 seems to override the custom error page completely, and shows its own status page (if you have one configured.)

Has anyone else seen this behavior and also maybe know how to work around it? It was working under IIS6, so I don't know why things changed.

Note: This is not the same as the issue in ASP.NET Custom 404 Returning 200 OK Instead of 404 Not Found

Community
  • 1
  • 1
Nicholas Head
  • 3,716
  • 5
  • 27
  • 39
  • I had the same question. Already answered here [http://stackoverflow.com/questions/347281/asp-net-custom-404-returning-200-ok-instead-of-404-not-found](http://stackoverflow.com/questions/347281/asp-net-custom-404-returning-200-ok-instead-of-404-not-found). – Bobby Cannon Jan 12 '09 at 02:54
  • Bobby, I actually found that question and tried it, but it didn't fix the problem. But thanks. – Nicholas Head Jan 12 '09 at 03:37
  • I'd like to comment that this issue also occurs when switching from Classic to Integrated pipeine. I used @PavelChuchuva solution (@RickStrahl solution also works). I'm guessing the "passthrough" in Classic is automatic, in Integrated it takes the server's global error page handling.. – sonjz Jan 16 '15 at 02:02

7 Answers7

118

Set existingResponse to PassThrough in system.webServer/httpErrors section:

  <system.webServer>
    <httpErrors existingResponse="PassThrough" />
  </system.webServer>

Default value of existingResponse property is Auto:

Auto tells custom error module to do the right thing. Actual error text seen by clients will be affected depending on value of fTrySkipCustomErrors returned in IHttpResponse::GetStatus call. When fTrySkipCustomErrors is set to true, custom error module will let the response pass through but if it is set to false, custom errors module replaces text with its own text.

More information: What to expect from IIS7 custom error module

Pavel Chuchuva
  • 22,633
  • 10
  • 99
  • 115
  • 3
    Note that setting existingResponse to PassThrough can lead to some side effect. Please master the link provided by Pavel before any changes. – Lex Li Feb 12 '10 at 08:09
  • Is `` equivalent to `Response.TrySkipIisCustomErrors` or do they behave differently? – Asbjørn Ulsberg Jun 30 '11 at 09:12
  • 1
    @sbjornu They achieve the same thing but with `Response.TrySkipIisCustomErrors` you get better control when to display IIS custom errors. – Pavel Chuchuva Jul 03 '11 at 23:13
  • thank you, i have seen a lot of info about response.tryskipiiscustomerrors but not so much about existingresponse. – HBCondo Feb 22 '12 at 21:05
  • I solved a problem with custom error pages not working on my web host running IIS7 simply by setting existingResponse="Auto", which was very surprising since the article referenced claims that is the default. It clearly isn't... or my hosting company set the wrong default elsewhere, I suppose. Either way I hope that saves someone a few hours :\ – Eric Sassaman Apr 29 '13 at 16:24
  • 2
    Even though, we had HttpContext.Current.Response.TrySkipIisCustomErrors = true; but that didn't work. But following this solved my issue. – Abhishek Shrivastava Jun 05 '14 at 20:37
83

The easiest way to make the behavior consistent is to clear the error and use Response.TrySkipIisCustomErrors and set it to true. This will override the IIS global error page handling from within your page or the global error handler in Application_Error.

Server.ClearError();
Response.TrySkipIisCustomErrors = true;

Typically you should do this in your Application_Error handler that handles all errors that your application error handlers are not catching.

More detailed info can be found in this blog post: http://www.west-wind.com/weblog/posts/745738.aspx

Rick Strahl
  • 17,302
  • 14
  • 89
  • 134
  • 2
    This also doesn't work for me (IIS8), and the advice doesn't seem to match the OP (assuming I'm reading it correctly). I **want** the `customError` configured in Web.config to trigger. With `Response.TrySkipIisCustomErrors = true` I get the same behavior: The ugly server-generated error page is displayed. With it set to `false` nothing happens - a blank browser window. – Shawn South Feb 26 '14 at 00:10
  • Worked fine for me! While the setting that Pavel Chuchuva mentions in his answer worked as well, it had some side effects that caused other issues. This setting let me skip the IIS error override in the specific scenario I wanted, while leaving the behavior intact for everything else. – Kevin Tighe May 06 '15 at 15:23
  • Worked well in Azure for me. Server headers `Server:Microsoft-IIS/8.5 X-AspNet-Version:4.0.30319 X-AspNetMvc-Version:5.2 X-Powered-By:ASP.NET` – oxfn Oct 05 '15 at 13:10
  • I still find I need to set `customErrors mode="Off"` for this to work. If I do that, then the httpErrors existingResponse="Auto"(the default) works properly for me when I use the code in this answer. – AaronLS Jun 17 '19 at 19:46
11

Solved: It turns out that "Detailed Errors" needs to be on in order for IIS7 to "passthrough" any error page you might have. See http://forums.iis.net/t/1146653.aspx

Nicholas Head
  • 3,716
  • 5
  • 27
  • 39
  • 1
    Though this one was marked as the answer, I think it worths the while to read other replies to gain more insights on the topic. – Lex Li Feb 12 '10 at 08:12
  • Also it could be good to remove. HandleErrorAttribute in FilterConfig – Per G Jun 26 '13 at 15:36
4

I'm not sure if this is similar in nature or not, but I solved an issue that sounds similar on the surface and here's how I handled it.

First of all, the default value for existingResponse (Auto) was the correct answer in my case, since I have a custom 404, 400 and 500 (I could create others, but these three will suffice for what I'm doing). Here are the relevant sections that helped me.

From web.config:

<customErrors mode="Off" />

And

<httpErrors errorMode="Custom" existingResponse="Auto" defaultResponseMode="ExecuteURL">
  <clear />
  <error statusCode="404" path="/errors/404.aspx" responseMode="ExecuteURL" />
  <error statusCode="500" path="/errors/500.aspx" responseMode="ExecuteURL" />
  <error statusCode="400" path="/errors/400.aspx" responseMode="ExecuteURL" />
</httpErrors>

From there, I added this into Application_Error on global.asax:

    Response.TrySkipIisCustomErrors = True

On each of my custom error pages I had to include the correct response status code. In my case, I'm using a custom 404 to send users to different sections of my site, so I don't want a 404 status code returned unless it actually is a dead page.

Anyway, that's how I did it. Hope that helps someone.

SEFL
  • 539
  • 4
  • 15
3

This issue has been a major headache. None of the suggestions previously mentioned alone solved it for me, so I'm including my solution. For the record, our environment/platform uses:

  • .NET Framework 4
  • MVC 3
  • IIS8 (workstation) and IIS7 (web server)

Specifically, I was trying to get an HTTP 404 response that would redirect the user to our custom 404 page (via the Web.config settings).

First, my code had to throw an HttpException. Returning a NotFoundResult from the controller did not achieve the results I was after.

throw new HttpException(404, "There is no class with that subject");

Then I had to configure both the customErrors and httpError nodes in the Web.config.

<customErrors mode="On" defaultRedirect="/classes/Error.aspx">
  <error statusCode="404" redirect="/classes/404.html" />
</customErrors>

...

<httpErrors errorMode="Custom" existingResponse="Auto" defaultResponseMode="ExecuteURL">
  <clear />
  <error statusCode="404" path="/classes/404.aspx" responseMode="ExecuteURL" />
</httpErrors>

Note that I left the existingResponse as Auto, which is different than the solution @sefl provided.

The customErrors settings appeared to be necessary for handling my explicitly thrown HttpException, while the httpErrors node handled URLs that fell outside of the route patterns specified in Globals.asax.cs.

P.S. With these settings I did not need to set Response.TrySkipIisCustomErrors

Shawn South
  • 597
  • 3
  • 17
2

TrySkipIisCustomErrors is only a part of a puzzle. If you use Custom Error Pages but you also want to deliver some RESTful content based on 4xx statuses then you have a problem. Setting web.config's httpErrors.existingResponse to "Auto" does not work, because .net seems to always deliver some page content to IIS, therefore using "Auto" causes all (or at least some) Custom Error Pages to be not used. Using "Replace" won't work too, because response will contain your http status code, but its content will be empty or filled with Custom Error Page. And the "PassThrough" in fact turns the CEP off, so it can't be used.

So if you want to bypass CEP for some cases (by bypassing I mean returning status 4xx with some content) you will need additional step: clean the error:

void Application_Error(object sender, EventArgs e)
{
    var httpException = Context.Server.GetLastError() as HttpException;
    var statusCode = httpException != null ? httpException.GetHttpCode() : (int)HttpStatusCode.InternalServerError;

    Context.Server.ClearError();
    Context.Response.StatusCode = statusCode;
}

So if you want to use REST response (i.e. 400 - Bad Request) and send some content with it, you will just need to set TrySkipIisCustomErrors somewhere in action and set existingResponse to "Auto" in httpErrors section in web.config. Now:

  • when there's no error (action returns 4xx or 5xx) and some content is returned the CEP is not used and the content is passed to client;
  • when there's an error (an exception is thrown) the content returned by error handlers is removed, so the CEP is used.

If you want to return status with empty content from you action it will be treated as an empty response and CEP will be shown, so there's some room to improve this code.

0

By default IIS 7 uses detailed custom error messages so I would assume that Response.StatusCode will equal 404.XX rather than just 404.

You can configure IIS7 to use the simpler error message codes or modify your code handling the more detailed error messages that IIS7 offers.

More info available here: http://blogs.iis.net/rakkimk/archive/2008/10/03/iis7-enabling-custom-error-pages.aspx

Further investigation revealed I had it the wrong way around - detailed messages aren't by default but perhaps they've been turned on, on your box if you're seeing the different error messages that you've mentioned.

nullnvoid
  • 562
  • 1
  • 4
  • 10
  • Response.StatusCode is an integer, so I don't see a way of setting a more specific code than just "404". I've got IIS7 configured to use/show custom error pages, like your URL indicates. – Nicholas Head Jan 12 '09 at 03:45
  • Hmmm...Unfortunately I can't test at the moment as I'm not on my home pc. If you don't have a solution by then - I will have a look tonight. – nullnvoid Jan 12 '09 at 03:49