IIS7 Overrides customErrors when setting Response.StatusCode? - asp.net

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

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

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

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

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.

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

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.

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.

Related

MVC Catch 404 errors for urls with file extensions without using RAMMFAR

I've done a bit of research into HttpHandlers and HttpModules and I'm not clear on the best way to implement this to my specifications.
My issue is that the StaticFile handler handles all urls with extensions and will return a default IIS 404 page instead of visiting the NotFound action of my ErrorsController. I want to use MVC to handle all 404s because of the benefits of using .cshtml pages over .html pages along with the fact that I perform logging and of all 404 responses and the pages contain some dynamic content.
I don't want to enable <modules runAllManagedModulesForAllRequests="true"> because of the performance impact it causes and because it seems like the wrong way to do this.
Is it plausible to create an HttpModule and have it check for the existence of the requested file and, if not found, send the request to the managed pipeline and eventually to my ErrorsController to handle the request?
My solution here handles all 404s from unauthenticated users, this includes all urls with file extensions. The nice thing about this solution is existingResponse='Auto' determines if a 404 is already handled by the ErrorController and if so it will skip this step but if not, use the provided custom error page.
You will have to skip IIS Custom errors with Response.TrySkipIisCustomErrors = true;.
You might have to set the status code again in the method using Response.StatusCode = (int)HttpStatusCode.NotFound;
In your ErrorController
[HttpGet]
public ActionResult 404()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
Response.TrySkipIisCustomErrors = true;
return View("Index", new ErrorModel
{
//Fill in properties values you have in your ErrorModel.
});
}
In your webconfig.
<httpErrors errorMode="Custom" existingResponse="Auto">
<remove statusCode="404" />
<error statusCode="404" path="/Error/404" responseMode="ExecuteURL" />
</httpErrors>

Why do some 'potentially dangerous Request.Path' HttpExceptions ignore httpErrors settings?

I have a .Net website with custom error pages configured using the web.config httpErrors section:
<httpErrors errorMode="Custom" existingResponse="Replace">
<clear/>
<error statusCode="400" path="/page-not-found/" responseMode="ExecuteURL" />
<error statusCode="404" path="/page-not-found/" responseMode="ExecuteURL" />
<error statusCode="500" path="/error/" responseMode="ExecuteURL" />
</httpErrors>
and
<httpRuntime requestValidationMode="2.0" targetFramework="4.5" />
When I visit http://www.example.com/< the site displays the correct "page not found" page, however if I visit http://www.example.com/<a it displays a YSOD with a 500 status code response header.
I have Elmah hooked up and both URLs predictably throw exactly the same error System.Web.HttpException: A potentially dangerous Request.Path value was detected from the client (<).
But why are the two URLs not handled the same? Why is one rewritten to my custom error page, and the other not? I would expect the same behaviour from any exception.
Edit:
I should have mentioned up front that this was an Umbraco project. I thought that this wasn't a contributing factor but I created a barebones .Net project with no dependencies and this worked as expected, i.e. both URLs obeyed the httpErrors configuration. So this must be something Umbraco specific, probably a module.
The validate helper method first calls validation for input and then paths.
The default values that are set in the default configuration for the validate helper method when validating input from the default web.config setting located at "syste.web/httRuntime" requestPathInvalidCharactersArray attribute are:
<,>,*,%,&,:,\,?
source: https://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.requestpathinvalidcharacters(v=vs.110).aspx
As you can see "<" is being treated as an invalid PATH whereas "<a" would be treated as invalid INPUT and that is why you get two behaviors.
If you're curious as to how I figured this out, I just looked at the source code for the validate helper method and followed it back to the source.
[EDIT]
The ValidateInputIfRequiredByConfig Method shows it throwing a 400
[EDIT 2]
public NameValueCollection QueryString
{
get
{
this.EnsureQueryString();
if (this._flags[1])
{
this._flags.Clear(1);
this.ValidateHttpValueCollection(this._queryString, RequestValidationSource.QueryString);
}
return this._queryString;
}
}
Calls to this property hit the ValidateHttpValueCollection can validate the querystring if the validation source is Querystring, which it is in this property.
In this method there is a method called ValidateString. In ValidateString it determines if it's a valid string or not and if not it throws a HttpRequestValidationException
After a fair amount of digging, I can see that this was actually caused by a 3rd party module called ClientDependency. Removal of this and the normal behaviour returned but obviously in this case the backoffice will not function properly.
A patch on the project is required and I've submitted this.
Update:
This bug in ClientDependency has been fixed as of v1.8.3

Is it necessary to log http 404 exceptions

I have below code in global application error event. If there is any invalid url, this event gets fired. Is it required to log http 404 errors.
void Application_Error(object sender, System.EventArgs e)
{
Exception lastException = HttpContext.Current.Server.GetLastError();
//log exception
}
The question is quite general, but logging 404 pages is quite good for several reasons.
By logging 404 pages you can find broken links on your web site and also to see how frequent it happens, that way you can create redirects if needed.
But it's your choice of course if you want to that or not.
Most servers are configured to write 404s along with all other requests and responses to its access log.
It's on your own if you want to log this error. Errors can also be handled automaticly using web.config inside system.web section, like this:
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
Basicly server will automaticly redirect to declared pages on specific error number.
The answer to your question is quite simply: "No, it's not required to log 404 errors."
It is best practice to have a process in place for handling 404 errors to maximize the search engine optimization of your site, though.

how to redirect 404 (bad urls) to the homepage

I am using asp.net and when I type a bad url manually(in the browser) it gives me:
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
I want a bad url that doesn't exist to be re-directed to the home page.
How do I do this? I am using sitemap.
If you have no intentions of letting the users know, they are being redirected. Then, you could just turn custom errors on and do something like this:
<configuration>
<system.web>
<customErrors defaultRedirect="default.aspx" mode="On">
<error statusCode="404" redirect="default.aspx"/>
</customErrors>
</system.web>
</configuration>
As others have already answered, web.config is one way to go.
The other is to catch unhandled exceptions from within your application. This gives you more control of the redirect.
protected void Application_Error(object sender, EventArgs e)
{
HttpException httpException = Server.GetLastError() as HttpException;
if (httpException.GetHttpCode() == 404)
Response.Redirect("/MainPage.aspx");
}
Remember that if you create your own 404-page you must:
Add 404-code to the Response manually.
Keep the reply body above 512 bytes or the browser will show its default error message instead.
Add this section to your web.config:
<customErrors mode="On" defaultRedirect="{yourDefaultErrorPage}">
<error statusCode="404" redirect="{yourhomePage}"/>
</customErrors>
customErrors Element on MSDN.
If you can, try have your 404 pages permanent redirect to a similar URL.
So instead of 404 response, make a 301 response to a similar URL on your site. Best SEO wise

ASP.net HTTP 404 - File not found instead of MaxRequestLength exception

I have a file upload control on my webpage. The maximum request length is set to 8 MB (maxRequestLength = 8192). I also have server validation that throws an error if the file is more than 4MB. The reason that its 8MB in the config is the leverage that's given to the user and also so that the application can be tested.
If I upload a file that's 9MB, I get thrown an exception Maximum request length exceeded., which is fine and working as expected. But when I try to upload a file that's 1GB, it shows me a HTTP 404 - File not found. Can someone please explain why this is happening and how can I get it to throw me a maxRequestLength exception?
I'm using IIS6.
I experienced this condition today (HTTP 404 on large file upload with IIS 7) but I thought I had made all the correct configuration settings. I wanted to upload files up to 300MB so I made the following web.config settings in a sub-folder of the application:
<configuration>
<system.web>
<httpRuntime maxRequestLength="307200" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="314572800" />
</requestFiltering>
</security>
</system.webServer>
</configuration>
This configuration worked in test but when I copied the updated files including the web.config to the production server, I received the HTTP 404 error on uploading a 90MB file. Smaller files under the application-wide limit of 30MB were working fine, so I knew it was a request size problem of some sort.
I figured there was a chance IIS had cached some application settings and just hadn't updated them, so I recycled the Application Pool, after which everything worked as expected.
I feel none of the answers here explain why you get a 404, they just tell you the usual stuff of how to fix the problem.
The 404 is not due to misconfiguration, it is intentional and documented behaviour:
When request filtering blocks an HTTP request because an HTTP request exceeds the request limits, IIS 7 will return an HTTP 404 error to the client and log one of the following HTTP statuses with a unique substatus that identifies the reason that the request was denied:
HTTP Substatus Description
404.13 Content Length Too Large
404.14 URL Too Long
404.15 Query String Too Long
These substatuses allow Web administrators to analyze their IIS logs and identify potential threats.
In addition, when an HTTP request exceeds the header limits that are defined in the in the <headerLimits> element, IIS 7 will return an HTTP 404 error to the client with the following substatus:
HTTP Substatus Description
404.10 Request Header Too Long
This is a bit of an old thread, but I thought I should add my experiences with this.
I faced the same problem with large file uploads and the web api. A 404.13 is thrown before it gets to a controller at all, so I had to find out where to jump in and handle this case.
My solution was the following web.config entries:
I handle the 404.13 by redirecting it to a mvc controller (it could be a webforms page just the same), and regular 404 errors hit my 404 route. it's critical that the responseMode="redirect" for the 404.13
<httpErrors errorMode="Custom">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" subStatusCode="13" path="/errors/filesize" responseMode="Redirect" />
<error statusCode="404" path="/errors/notfound" responseMode="ExecuteURL" />
</httpErrors>
Then, in my Errors controller, I have the following:
public ActionResult FileSize()
{
Response.StatusCode = 500;
Response.StatusDescription = "Maximum file size exceeded.";
Response.End();
return null;
}
Again, this could be a regular webforms page.
To my knowledge, there is no way to gracefully handle exceeding IIS's "maxRequestLength" setting. It can't even display a custom error page (since there is no corresponding HTTP code to respond to). The only way around this is to set maxRequestLength to some absurdly high number of kbytes, for example 51200 (50MB), and then check the ContentLength after the file has been uploaded (assuming the request didn't time out before 90 seconds). At that point, I can validate if the file <=5MB and display a friendly error.
You can also try this link.
You could also try something like this:
private void application_EndRequest(object sender, EventArgs e)
{
HttpRequest request = HttpContext.Current.Request;
HttpResponse response = HttpContext.Current.Response;
if ((request.HttpMethod == "POST") &&
(response.StatusCode == 404 && response.SubStatusCode == 13))
{
// Clear the response header but do not clear errors and transfer back to requesting page to handle error
response.ClearHeaders();
HttpContext.Current.Server.Transfer(request.AppRelativeCurrentExecutionFilePath);
}
}
I have found that this problem can also be caused on IIS7 (and presumably IIS6) when the URLScan tool is installed and running on the site.
When uploading the file to a website i was receiving the message "File or directory not found. The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable."
If the problem is being caused by URLScan then if you try to upload the large file to the site whilst browsing the site on the hosting server itself, you will be served a full asp.net error message instead of a 404 that mentions URLScan.
You can also check if URLScan is running on you site in IIS7 by viewing the ISAPI Filters for the website in IIS, URLScan will be listed if it is used.
This can be fixed by altering the ini file for URLScan is located at "%WINDIR%\System32\Inetsrv\URLscan" and changing the MaxAllowedContentLength.
The MaxAllowedContentLength is in bytes.
This may require a IIS restart to take effect, though it did not when i tried it myself with IIS7.
http://www.iis.net/learn/extensions/working-with-urlscan/urlscan-overview
http://www.iis.net/learn/extensions/working-with-urlscan/common-urlscan-scenarios
You could configure the default error page in IIS itself.
The request limit is a setting is IIS. Open the Request Filtering section of your site in IIS and select Edit Request Settings. For me it was that simple.
A more detailed How To from Microsoft.
https://learn.microsoft.com/en-us/iis/configuration/system.webserver/security/requestfiltering/#how-to-edit-the-request-filtering-feature-settings-and-request-limits
I just met the same problem, i made the similar operation like pseudocoder's answer but have different( i think maybe is not the cache) :
edit your Web.config --> maxRequestLength
<system.web>
<httpRuntime maxRequestLength="1073741824" executionTimeout="3600" />
</system.web>
edit this:
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
just like this,and try it.
The problem with the 1GB uploads is more browser related. I have had heaps of trouble with it and tried a lot of solutions but really the question to ask here is what are the chances of this happening in the real world for your business needs and maybe it should be recorded as a known issue in the business rules or non functional requirements document.

Resources