Why this matters for PCI DSS
A verbose error page is a free reconnaissance brief. From a single stack trace an attacker learns: framework + version (which CVEs apply), filesystem layout (which paths to probe), database schema (which tables exist), and often credentials embedded in connection strings.
SQL error messages are particularly serious — they confirm the presence of an injection point and reveal the schema the attacker needs to exploit it.
PCI DSS 4.0 Requirement 6.2.4 requires defenses against information disclosure. Requirement 6.5 covers application coding standards including error handling.
How to fix it
The principle is the same in every stack: log the full detail to a server-side log; show the user a generic page.
PHP (php.ini, production):
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/error.log
ASP.NET / IIS (web.config):
<system.web>
<customErrors mode="On" defaultRedirect="~/error.aspx" />
<compilation debug="false" />
</system.web>
<system.runtime.remoting>
<customErrors mode="on" />
</system.runtime.remoting>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace" />
</system.webServer>
Rails (config/environments/production.rb):
config.consider_all_requests_local = false
config.action_dispatch.show_exceptions = true
Django (settings.py):
DEBUG = False
ALLOWED_HOSTS = ['example.com']
Don't forget custom 404 and 500 templates.
Express — register a final error-handling middleware that hides the stack:
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Sorry, something went wrong.');
});
Legacy ASP.NET remoting — if the application does not use .NET Remoting over HTTP, remove the IIS mappings for *.rem and *.soap. If it is genuinely required, keep both ASP.NET and remoting custom errors enabled for remote callers.
Web server defaults — also configure custom error pages at the web server level so a crashed application doesn't fall back to nginx/Apache/IIS default error pages, which themselves leak version info.
Verify both services with non-existent paths: curl -i http://example.com/this-does-not-exist and curl -i https://example.com/this-does-not-exist — each response should redirect cleanly or show a sanitised error page, never a stack trace. A bodyless curl -i https://example.com/pciscan-remoting-test.rem must not return the concrete System.Runtime.Remoting.RemotingException type to a remote client.