The default framework error page in production often renders stack traces, internal file paths, and framework version information directly in the browser — feeding an attacker a precise map of the application's internal structure (CWE-209). OWASP A05 lists this as a Security Misconfiguration. Equally important: an error page that shows a friendly message but discards the error object silently means production failures are invisible. You need both — a safe user-facing message and server-side error capture so you know the failure happened.
High because a default or stack-trace-rendering error page exposes application internals per CWE-209, while a completely silent error boundary leaves production failures undetected.
Create app/error.tsx and app/global-error.tsx. Log the error server-side or to your monitoring service; show only a generic message to the user. Never render error.message or error.stack in the UI.
// app/error.tsx
'use client'
import { useEffect } from 'react'
export default function Error({
error, reset,
}: { error: Error & { digest?: string }; reset: () => void }) {
useEffect(() => { console.error(error) }, [error])
return (
<main>
<h1>Something went wrong</h1>
<p>We've been notified and are working on a fix.</p>
<button onClick={() => reset()}>Try again</button>
</main>
)
}
Replace console.error with your monitoring service SDK (Sentry, Bugsnag) for production alerting. The digest prop is a safe server-side error ID you can log — it never exposes the original stack trace to the client.
ID: pre-launch.user-facing.custom-500
Severity: high
What to look for: Count all error boundary and error page implementations. Enumerate whether a custom 500/error page exists with user-friendly messaging. Check for custom error/500 pages: app/error.tsx and app/global-error.tsx in Next.js App Router, pages/500.tsx in Pages Router, or equivalent. Inspect the error page component to verify it does not render error details, stack traces, or internal paths when displayed to users. Check that the component accepts the error prop but does not render error.message or error.stack directly.
Pass criteria: A custom error page exists that displays a generic user-friendly message without rendering error details, stack traces, or internal file paths. The error page must BOTH (1) hide technical details from users AND (2) capture the error for server-side observability. At minimum, the error boundary should call console.error(error) in a useEffect (for client components) or log to a monitoring service. An error page that renders a friendly message but silently discards the error object is a partial implementation — note in the detail that error capture is missing if only condition (1) is satisfied. At least 1 custom error page must exist with user-friendly messaging.
Fail criteria: No custom error page found, or a custom error page exists but renders error.message or error.stack directly in the UI.
Skip (N/A) when: Never — every web project should have a safe error handling page.
Cross-reference: For custom 404, see custom-404. For error monitoring, see error-monitoring.
Detail on fail: "No custom error page found, relying on framework default" or "Custom error.tsx renders error.message directly in the UI — stack traces visible to users in production"
Remediation: The default framework error page may expose stack traces and internal paths to users. Create a custom error boundary that logs internally but shows nothing sensitive:
// app/error.tsx — custom error page
'use client'
export default function Error({ reset }: { reset: () => void }) { return <div><h1>Something went wrong</h1><button onClick={reset}>Try Again</button></div> }
// app/error.tsx
'use client'
import { useEffect } from 'react'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// Log to your error monitoring service
console.error(error)
}, [error])
return (
<main>
<h1>Something went wrong</h1>
<p>We've been notified and are working on a fix.</p>
<button onClick={() => reset()}>Try again</button>
</main>
)
}
Never render error.message or error.stack in the UI — log these server-side or to your monitoring service. For a deeper analysis of error exposure, the Security Headers & Basics Audit covers information disclosure in detail.