Access and configuration
Fail without leaking internals
Return useful public errors while keeping stack traces, queries, paths, tokens, and infrastructure details in protected diagnostics.
Errors help users recover and help attackers learn. A stack trace that reaches the browser can name your database, file paths, internal hostnames, and library versions. Separate the public message from the private diagnostic record.
A database outage returns a stack trace containing the SQL query, filesystem path, and internal host. The error helps the user no more than a safe failure would.
Log the detail, return a code
Catch failures at a central handler. Record everything internally, and send the client only a stable code and a request ID that ties the two together.
app.use((err, req, res, next) => {
const requestId = req.id
console.error({ requestId, message: err.message, stack: err.stack }) // stays server-side
res.status(500).json({ error: 'internal_error', requestId })
})
The user can quote the request ID to support, and support can find the full context, but the response body reveals nothing about the internals.
Make sure the framework’s development error page is off in production. In Express, NODE_ENV=production already stops the default handler from sending stack traces to the client.
NODE_ENV=production node server.js
Removing all detail can make support impossible. Return a stable public code and request ID while keeping the protected diagnostic record.
Test unexpected exceptions, not only clean validation failures, since those are the ones that leak.
Trigger validation, storage, and unexpected runtime failures in production mode. Save public responses and internal events, then prove the response contains no stack, query, path, host, or secret while the request ID connects both sides.
Lesson completed