How to Prevent IIS from Overriding Custom HTTP Status Codes and Response Content in ASP.NET MVC


1 views

When working with ASP.NET MVC applications, developers often encounter a frustrating behavior where IIS replaces their custom error responses with its default error pages. This occurs specifically when manually setting HTTP status codes like 404 or 503 in the controller.

// Problematic scenario example
public ActionResult NotFound()
{
    Response.StatusCode = 404;
    return View("Custom404"); // IIS shows its own 404 page instead
}

IIS has its own custom error handling system that kicks in when it detects certain HTTP status codes. This system is designed to provide consistent error pages across applications, but it often interferes with developer intentions.

1. Disabling IIS Custom Errors

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

This configuration tells IIS to pass through your custom responses unchanged.

2. Using Response.TrySkipIisCustomErrors

public ActionResult CustomError()
{
    Response.StatusCode = 404;
    Response.TrySkipIisCustomErrors = true;
    return View("MyErrorView");
}

3. Combined Web.config Approach

For maximum compatibility, combine both methods:

<configuration>
    <system.web>
        <customErrors mode="Off"/>
    </system.web>
    <system.webServer>
        <httpErrors existingResponse="PassThrough" />
    </system.webServer>
</configuration>

For applications using ASP.NET Core hosted in IIS:

// In Configure method
app.Use(async (context, next) => {
    context.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Custom Message";
    await next();
});

Always verify your solution works in both development and production environments. The Cassini/IIS Express behavior often differs from full IIS.

// Test case example
[TestMethod]
public void Should_Return_Custom_404_Page()
{
    // Arrange
    var controller = new HomeController();
    
    // Act
    var result = controller.NotFound() as ViewResult;
    
    // Assert
    Assert.AreEqual(404, controller.Response.StatusCode);
    Assert.AreEqual("Custom404", result.ViewName);
}

When developing ASP.NET MVC applications, you might encounter a frustrating behavior where IIS overrides your custom HTTP status codes (like 404 or 503) with its own stock error pages. This occurs even when you explicitly set Response.StatusCode in your controller.

// This gets overridden by IIS
public ActionResult NotFoundPage()
{
    Response.StatusCode = 404;
    return View("CustomNotFound");
}

IIS has built-in error handling that intercepts certain HTTP status codes before they reach the client. This feature is designed to provide consistent error pages across applications, but it interferes with custom error handling in MVC.

Add this to your web.config to prevent IIS from intercepting your status codes:

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

For more granular control, use this in your controller:

public ActionResult MaintenanceMode()
{
    Response.StatusCode = 503;
    Response.TrySkipIisCustomErrors = true;
    return View("MaintenanceView");
}

Here's a full implementation for custom 404 handling:

// In Global.asax or Startup.cs
routes.MapRoute(
    name: "404-catch-all",
    url: "{*url}",
    defaults: new { controller = "Error", action = "NotFound" }
);

// ErrorController.cs
public class ErrorController : Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;
        Response.TrySkipIisCustomErrors = true;
        return View();
    }
}

After implementing these solutions, test with:

  1. Development server (should work natively)
  2. IIS Express (may need configuration)
  3. Full IIS (requires web.config changes)

Remember that some HTTP modules or third-party components might still interfere with your custom status codes. Always test in your production-like environment.