Fixes
Public Exposure · Updated 2026-05-02
Error Handling Quality
Database errors and stack traces in 4xx/5xx pages are the worst leaks. Use custom error pages that show no internal detail.
Error pages routinely leak more than the error itself: stack traces with full source paths, database error messages with table names and parameter values, and framework-default "Whitelabel Error" pages that confirm the stack to an attacker. This check probes for an HTTP error response (4xx or 5xx) and grades whatever body comes back. The worst tier is database error messages exposed verbatim; just behind that are stack traces and framework debug pages.
How the check works
Per primary host, the check looks for an error-class response and scores its body. If the baseline web probe did not hit an error path, the check makes synthetic attempts (unlikely GET path, OPTIONS /, invalid method) to provoke one. If no error response can be obtained even after synthetic probes, the check defers with no score; otherwise it scores against the worst body observed.
Per-host deductions out of a 25-point budget:
- Database error in body (deduction 25, reason database_error_exposed): SQL exceptions, ORM error messages, table names, query fragments. Worst tier; usually means the application is leaking exception messages straight to the client.
- Stack trace or framework error page (deduction 20, reason stack_trace_exposed): tracebacks, framework-default error views (Whitelabel, Werkzeug, ASP.NET YSOD, Symfony Profiler error page). Reveals internal source paths, library versions, and surrounding context.
- Default server error page (deduction 8, reason default_error_page): nginx/Apache/IIS default 404/500/502 pages, or a 4xx/5xx response with no body and no custom error setup.
- No error response could be provoked: deferred (no score). The check gets out of the way rather than penalizing you for a robust setup it could not test.
Score = (25 - deduction) / 25. Per-host scores are weighted (apex highest) and averaged. Verdict thresholds: pass at 0.9 and above, warn at 0.45 and above, fail below.
How the verdict maps to evidence
- Pass: error path returned a clean custom error page on every primary host.
- Warn: default-server-page tier (no body, or generic server default) on at least one host.
- Fail: stack trace or database error visible on at least one host.
- Deferred (no score): no 4xx/5xx response could be provoked even with synthetic probes. Not penalized.
Evidence shows status, body length, and which reason code (if any) fired. When synthetic probing was used, the evidence note flags it.
Special states
- Deferred: no error path could be exercised. Re-runs may catch a real error path if one exists later.
- Degraded: probe data unavailable. Fix Web Assessability first.
Fix by reason code
database_error_exposed (deduction 25, treat as critical)
A database driver or ORM exception is being rendered to the client verbatim. The exception message often includes the SQL statement, parameter values, table names, and database server type and version. The fix is application-side: catch and log database exceptions server-side, return a generic error to the client.
Pattern: wrap DB calls in catch-and-generic-respond
// Express / Node.js example
app.get("/api/users/:id", async (req, res) => {
try {
const user = await db.users.findById(req.params.id);
res.json(user);
} catch (err) {
console.error("DB error", err); // log full error server-side
res.status(500).json({ error: "internal_error" }); // generic to client
}
});
// At the framework level, register a global error handler:
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "internal_error" });
});stack_trace_exposed (deduction 20)
A framework-default error page or a raw stack trace is in the response body. Per-framework fixes:
- Symfony: APP_ENV=prod and APP_DEBUG=0; ensure twig:dump and the profiler bundle are not in production composer install. (See Debug Indicators.)
- Spring Boot: server.error.include-stacktrace=never and server.error.whitelabel.enabled=false; provide a custom error controller that returns a generic body.
- ASP.NET: <customErrors mode="On" defaultRedirect="/error" /> in web.config; ASP.NET Core: app.UseExceptionHandler("/Error") outside Development environment.
- Django: DEBUG = False AND ALLOWED_HOSTS set. Without both, debug error pages are served to anyone.
- Rails: config.consider_all_requests_local = false in production.rb.
- Express / Node.js: register a global error middleware that returns generic responses; never call res.send(err.stack).
- Flask: app.config["DEBUG"] = False and run via gunicorn / uWSGI, not the Werkzeug dev server. (See Debug Indicators.)
default_error_page (deduction 8)
Either a 4xx/5xx with no body, or a server-default error page (nginx "404 Not Found" with the version footer, Apache test page-style errors). Configure custom error pages at the web server or load balancer:
nginx
server {
error_page 404 /errors/404.html;
error_page 500 502 503 504 /errors/5xx.html;
location /errors/ {
internal;
root /var/www;
}
}Apache
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/5xx.html
ErrorDocument 502 /errors/5xx.html
ErrorDocument 503 /errors/5xx.htmlAWS CloudFront (Custom Error Responses)
In the distribution → Error Pages tab:
HTTP Error Code: 404
Response Page Path: /errors/404.html
HTTP Response Code: 404
Repeat for 500, 502, 503, 504. Cache TTL ~ 5 minutes.Verify the fix
- curl -i https://yourdomain.tld/this-path-does-not-exist-12345 should return your custom 404 page with no stack trace, no source paths, no DB error.
- curl -i -X OPTIONS https://yourdomain.tld and curl -i -X TRACE https://yourdomain.tld can provoke 405 / 501 responses; verify those are also clean.
- Trigger a server-side exception in a controlled environment (a known-bad parameter on an internal endpoint, an intentionally-broken route in staging) and confirm the response body has no internals.
- Re-run the RedScore lookup. The verdict moves to pass when no error response on any primary host shows database, stack-trace, or default-page indicators.
Common pitfalls
- Stack traces only on specific routes. Default error handlers may catch most exceptions while one untyped route still leaks. Ensure the global error handler is the last middleware and that no per-route handler shortcuts it.
- Custom error page that includes the original error. Some custom 500 templates display the exception message directly to be "helpful". Strip the message; log it server-side, render a generic body to the client.
- JSON API returning err.stack on 500. The check looks at body content; JSON or HTML, the leak is the same. Wrap exception handlers to return { error: "internal_error" } in production.
- Synthetic probe path returning 200. If your app has a catch-all that returns 200 for unknown paths (single-page app fallback to index.html, for example), the check cannot provoke an error and may defer. The check is not penalizing you, but you should still hand-test error paths to confirm hygiene.
- Different error handling per status code. 404 may be clean while 500 leaks. Audit each error class.
- Framework-default error pages confused with custom ones. Spring Boot's Whitelabel page looks reasonably professional, but the check fingerprints it. Replace with a custom handler.
What to do next
See how these recommendations apply to your site's current scan results.
Scan domain