Next.js production note · 8 minute read
Your headers() config is not proof that production is protected.
The common failure is not forgetting every header. It is shipping a plausible configuration, overwriting an existing route rule, and never checking the response that users actually receive.
The pitfall
Next.js combines matching header rules. Your deployment layer may not.
A Next.js application can define response headers in next.config.mjs, middleware, route handlers, or hosting configuration. A global /:path* rule may look correct in review while a proxy, CDN, redirect, static asset path, or later platform rule changes the final response.
There is a second trap: replacing an existing headers() function with a security snippet. That can silently remove cache, CORS, download, or route-specific rules. A safer change merges the new baseline into the project's current configuration and then checks the deployed routes.
Risky shortcut
export default {
async headers() {
return [{ source: "/:path*", headers: copiedHeaders }];
},
};This may replace rules the application already relies on. It also proves only what the source says—not what a browser receives after deployment.
A safer sequence
Inventory, merge, preview, verify.
- 1
Inventory the current rules
Read the existing Next.js config, middleware, route handlers, redirects, and hosting-layer headers before editing.
- 2
Merge instead of overwrite
Preserve route-specific cache, CORS, download, and application rules. Add only controls whose behavior you can explain.
- 3
Deploy a preview
Do not use production as the first integration test. Check authentication, checkout, embeds, uploads, analytics, and downloads on the preview.
- 4
Inspect final responses
Request the deployed homepage plus representative dynamic, redirected, and static routes. Verify exact response headers, not source intent.
- 5
Promote with rollback
Record the before state, changed files, acceptance checks, and the fastest way to restore the prior configuration.
Production check
Test the URL, not your confidence.
npx poszo-nextjs-security-headers https://your-preview.example.comThe dependency-free checker reports missing or materially weak baseline headers and exits non-zero. Run it against more than the homepage when route behavior differs.
Read and audit the starter ↗Why CSP is deliberately absent
A generic Content Security Policy can be worse than an explicit gap.
CSP must match the application's real scripts, styles, images, analytics, payment provider, authentication flow, and embeds. A copied policy can break checkout or login; an overly broad policy can create a false sense of protection. The starter therefore does not pretend that one global CSP is safe for every Next.js app.
Build CSP from an inventory of required origins and behaviors, deploy it in report-only mode where appropriate, inspect violations, test critical journeys, and only then enforce it. Keep the policy and its exceptions reviewable.
Acceptance check
What “done” means.
- ✓ Existing route-specific rules still behave as intended.
- ✓ Representative production responses contain the expected baseline headers.
- ✓ Authentication, checkout, embeds, analytics, and downloads still work.
- ✓ Any omitted control has a written application-specific reason.
- ✓ Rollback can restore the previous configuration without guesswork.