When configuring IIS 7 as a reverse proxy for Java applications (like Tomcat), many developers encounter an unexpected behavior where custom error pages get served with HTTP 200 status codes instead of the proper 404 response. This creates SEO issues and violates HTTP standards, as clients receive successful status codes for failed requests.
The root cause lies in IIS's default handling of custom error pages. When using responseMode="ExecuteURL"
, IIS internally rewrites the request to your error page URL, effectively treating it as a successful resource fetch. The HTTP status gets "lost in translation" between the reverse proxy and the error handler.
Here's the problematic part of typical web.config setups:
<httpErrors errorMode="Custom" existingResponse="Auto">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/error/404.htm" responseMode="ExecuteURL" />
</httpErrors>
We need to modify two aspects of the configuration:
- Change the response mode from ExecuteURL to File
- Ensure proper error document location
Here's the corrected configuration:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404"
path="D:\Websites\YourSite\error\404.htm"
responseMode="File" />
</httpErrors>
- Use absolute physical paths when specifying error documents
- Set
existingResponse="Replace"
to force status code preservation - Test with tools like Fiddler or browser DevTools to verify headers
- For dynamic content, consider creating a dedicated .aspx handler that manually sets the status code
If you need dynamic content while preserving status codes, create a handler:
<error statusCode="404"
path="/404handler.aspx"
responseMode="ExecuteURL" />
Then in 404handler.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
Response.StatusCode = 404;
Response.TrySkipIisCustomErrors = true;
// Your dynamic content generation here
}
- Clear IIS configuration cache after changes (
appcmd recycle apppool
) - Check for conflicting URL rewrite rules that might intercept error paths
- Verify file system permissions on static error documents
- Test both direct access and proxied access scenarios
When configuring custom error pages in IIS 7, you might encounter an unexpected behavior where your custom 404 page is served with an HTTP 200 (OK) status code instead of the correct 404 (Not Found). This happens particularly when IIS acts as a reverse proxy for backend applications (like Java Tomcat).
Search engines and API clients rely on proper HTTP status codes. A 200 response for missing pages can:
- Negatively impact SEO
- Cause confusion in API clients
- Break web crawler expectations
The issue occurs because of the responseMode="ExecuteURL"
setting in IIS configuration. This mode tells IIS to execute the error page as if it were a regular request, resulting in a 200 status.
Modify your web.config
to use either:
<httpErrors errorMode="Custom" existingResponse="Auto">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404"
path="/error/404.htm"
responseMode="File" />
</httpErrors>
Or for dynamic content:
<error statusCode="404"
path="/error/404.aspx"
responseMode="ExecuteURL"
prefixLanguageFilePath="" />
When using IIS as reverse proxy, add this rule before your proxy rule:
<rule name="Handle Custom 404s" stopProcessing="true">
<match url="^error/.+" />
<action type="CustomResponse" statusCode="404" />
</rule>
Use curl to verify the response:
curl -I https://yourdomain.com/nonexistent-page
Should return:
HTTP/1.1 404 Not Found
The File
response mode is most efficient for static error pages. For dynamic pages requiring server processing, use ExecuteURL
but ensure your application sets the proper status code.
- Clear your browser cache when testing
- Check IIS failed request tracing logs
- Verify file permissions on your error pages
- Ensure no other rewrite rules interfere