How to Configure Web.Config for Custom 404 Handling While Preserving 500 Error Details in ASP.NET


4 views

Many developers face this common IIS configuration dilemma: you want elegant custom 404 pages but still need to see detailed 500 errors during development. The current web.config approach redirects all errors, which isn't ideal for debugging.

Here's why the original configuration fails:

<system.web>
  <customErrors mode="On">
    <error statusCode="404" redirect="/Custom404.html" />
  </customErrors>
</system.web>

The mode="On" setting forces all unhandled errors to redirect, including 500 errors.

Here's the corrected web.config setup that solves both requirements:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="DetailedLocalOnly">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/Custom404.html" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
  <system.web>
    <customErrors mode="RemoteOnly" defaultRedirect="~/Error.aspx">
      <error statusCode="404" redirect="~/Custom404.html"/>
    </customErrors>
  </system.web>
</configuration>
  • errorMode="DetailedLocalOnly": Shows detailed errors locally while presenting custom pages remotely
  • mode="RemoteOnly": Only shows custom errors to remote clients
  • Separate handling for 404 errors while allowing 500 errors to display

For more complex applications, consider this ASPX error page example:

<%@ Page Language="C#" %>
<%
  Exception ex = Server.GetLastError();
  if (ex != null) {
    if (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404) {
      Response.Redirect("~/Custom404.html");
    }
    else {
      // Display detailed error for developers
      Response.Write("<h1>Error Details:</h1>");
      Response.Write("<pre>" + Server.HtmlEncode(ex.ToString()) + "</pre>");
    }
    Server.ClearError();
  }
%>

Verify your setup works correctly by:

  1. Accessing a non-existent page (should show Custom404.html)
  2. Triggering a server error (should show detailed error when accessed locally)
  3. Checking both scenarios from remote machines
  • Ensure custom error pages don't contain errors themselves
  • Remember to clear the error after handling it
  • Test with different browsers and devices

Many developers face this common IIS configuration dilemma: we want friendly 404 pages for missing resources but need detailed error output for debugging 500 errors. The standard approach often masks both error types unintentionally.

Your current web.config contains both ASP.NET () and IIS () error handlers. While this provides redundancy, it's causing the 500 error masking:



  
    
      
    
  
  
  
    
      
      
    
  

The key issues in this configuration are:

  • forces all errors to redirect
  • httpErrors' defaultPath acts as catch-all for unhandled status codes
  • ResponseMode="ExecuteURL" vs "Redirect" differences

Here's the corrected version that maintains 404 customization while showing 500 details:



  
    
      
    
  
  
  
    
      
      
      
      
      
    
  

For this to work properly:

  • Set customErrors mode="RemoteOnly" or "Off" during development
  • Use responseMode="ExecuteURL" (not "Redirect") for proper status codes
  • Explicit 500 error handler prevents default error page
  • Keep errorMode="DetailedLocalOnly" for local debugging

Create test endpoints to verify behavior:

// 404 Test
https://yoursite.com/nonexistent-page

// 500 Test
https://yoursite.com/throw-error.aspx
// With code:
<%
    Response.TrySkipIisCustomErrors = true;
    Response.StatusCode = 500;
    throw new ApplicationException("Test error");
%>