How to Disable Stack Trace Exposure in Tomcat Error Pages for Production Environments


6 views

When deploying Tomcat in AWS or any production environment, exposing stack traces in error pages creates security vulnerabilities by revealing:

  • Internal package structures
  • Class/method implementation details
  • Potential attack vectors
  • Sensitive framework information

Tomcat provides multiple ways to suppress stack traces in error responses:


<!-- Option 1: In server.xml -->
<Valve className="org.apache.catalina.valves.ErrorReportValve"
       showReport="false"
       showServerInfo="false"/>

Alternatively, create a custom error page:


<!-- In web.xml -->
<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/error.html</location>
</error-page>

For granular control, extend ErrorReportValve:


public class SecureErrorValve extends ErrorReportValve {
    @Override
    protected void report(Request request, Response response, Throwable throwable) {
        response.setStatus(getStatus(request, response, throwable));
        // Write minimal error info without stack trace
        response.getWriter().write("An error occurred");
    }
}

After configuration, test with:

  1. Trigger an application exception
  2. Verify HTTP 503 response
  3. Confirm absence of stack traces
  4. Check server logs for detailed errors
  • Combine with proper logging (Log4j/SLF4J)
  • Implement centralized error monitoring
  • Use HTTPS for all error responses
  • Consider HTTP security headers

When running Tomcat in production (especially on cloud platforms like AWS), you might notice that unhandled exceptions result in error pages displaying full Java stack traces. This exposes internal implementation details, including:

  • Class names and package structures
  • Method invocation sequences
  • Line numbers in source files

While stack traces are invaluable during development, they become a security risk in production because:

  1. They reveal application architecture to potential attackers
  2. May contain sensitive information in exception messages
  3. Violate the principle of least information disclosure

The most effective approach is to configure custom error pages in web.xml:

<error-page>
    <error-code>500</error-code>
    <location>/error/500.html</location>
</error-page>
<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/error/500.html</location>
</error-page>

For more control, implement a custom ErrorReportValve:

public class ProductionErrorReportValve extends ErrorReportValve {
    @Override
    protected void report(Request request, Response response, Throwable throwable) {
        // Only show error code, no stack traces
        response.sendError(response.getStatus());
    }
}

Then configure it in server.xml:

<Valve className="com.yourpackage.ProductionErrorReportValve"/>

Combine this with security headers in your web.xml:

<filter>
    <filter-name>httpHeaderSecurity</filter-name>
    <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
    <init-param>
        <param-name>antiClickJackingOption</param-name>
        <param-value>SAMEORIGIN</param-value>
    </init-param>
</filter>

After implementation, test by:

  1. Triggering a 500 error intentionally
  2. Checking response contains only HTTP status code
  3. Verifying no stack trace appears in HTML source