Next.js 16 CSP and Security Headers: A Production Guide for App Router
Ship a strict Content-Security-Policy, HSTS, Referrer-Policy, and Permissions-Policy on Next.js 16 App Router without breaking Server Actions, images, or analytics. Includes nonce patterns, report-only rollout, and Nginx notes for VPS hosts.
By Mussawar Hayat
Why Security Headers Still Fail on Next.js Sites
Most Next.js 16 apps ship with a default-open Content-Security-Policy or none at all. That leaves XSS, clickjacking, and mixed-content gaps even when Server Actions and auth look solid. This guide is how Mussawar Hayat rolls headers out on App Router products without breaking images, fonts, or form posts.
What You Will Learn
- Which headers matter in 2026 and which are noise
- How to set headers in next.config vs middleware vs Nginx
- Nonce-based CSP that still works with App Router scripts
- Report-Only rollout so you do not brick production
- How headers interact with Server Actions and third-party widgets
Minimum Header Set for a Production App Router Site
Start with a small, enforceable set. Add complexity only after Report-Only shows a clean week of traffic.
- Content-Security-Policy — default-src 'self'; script-src with nonces; img-src including your CDN and data: only if you truly need it
- Strict-Transport-Security — max-age=31536000; includeSubDomains; preload only after every subdomain is HTTPS
- X-Content-Type-Options — nosniff
- Referrer-Policy — strict-origin-when-cross-origin
- Permissions-Policy — camera=(), microphone=(), geolocation=() unless the product needs them
- X-Frame-Options — DENY or SAMEORIGIN; CSP frame-ancestors is the modern control
Do not invent custom header names. Browsers ignore them and operators waste time debugging ghosts.
Where to Set Headers in Next.js 16
Use the headers() function in next.config.ts for static policies that apply to all HTML routes. Use middleware only when the value must change per request, such as a per-request CSP nonce. On a VPS, duplicate HSTS and frame-ancestors at Nginx so static assets and old cached HTML still inherit the policy.
Self-hosted Next.js 16 standalone output pairs cleanly with this pattern. See the Docker multi-stage standalone guide and the multi-site Nginx VPS guide.
Nonce-Based CSP Without Breaking App Router
App Router injects scripts. A policy of script-src 'self' with no nonce or hash will block the framework runtime. Generate a cryptographically random nonce in middleware, attach it to the request headers, and read it in the root layout when rendering <script> tags you control.
Never put the nonce in a public environment variable. Never reuse one nonce across users or deploys. If you use Google Tag Manager or AdSense, give those origins an explicit host allowlist instead of 'unsafe-inline' whenever possible. 'unsafe-eval' should stay off in production.
Server Actions, Images, and Third Parties
Server Actions POST to the same origin. A CSP that blocks form posts or connect-src to self will fail silently in the browser console. Allow connect-src 'self' plus any API or RPC hosts the product actually calls.
next/image needs the optimizer path on your origin and every remote hostname listed in images.remotePatterns. If you patched the August 2026 image optimizer issues, keep remote patterns tight. Pair this article with the August 2026 security release notes and the Server Actions auth guide.
Report-Only Rollout
Ship Content-Security-Policy-Report-Only first. Send reports to an endpoint you own, not a public dump. Watch for blocked Next.js chunks, font CDNs, and preview images. After a clean window, flip the header to enforcing and keep Report-Only as a second, stricter draft policy.
Treat report volume like an error budget. A noisy policy that everyone ignores is worse than a slightly looser policy that is actually enforced.
CSP and Security Headers FAQ
Can I set CSP only in Nginx and skip Next.js?
You can for static sites. App Router HTML often needs a per-request nonce, which Nginx alone cannot mint into React markup. Use both layers.
Does HSTS preload belong on a brand-new domain?
No. Enable HSTS first. Submit to the preload list only after every subdomain is HTTPS and you accept the long lock-in.
Will a strict CSP break Server Actions?
Only if connect-src or form-action omit 'self', or if a script nonce fails to reach the layout. Fix those two first.
Should I still send X-XSS-Protection?
No. Modern browsers retired the header. Prefer CSP.
Summary
Headers are cheap compared with an XSS incident. Start with Report-Only, nonce scripts, tight image hosts, and HSTS. Enforce only after the reports are quiet.
Need this applied across a multi-site VPS? See full-stack and DevOps services or hire Mussawar Hayat.
Related guides
Next.js 16.3.3 and 15.5.24 patch two critical unauthenticated RCE issues: AVIF image optimization via sharp/libheif, and a Windows filesystem Server Action path. Here is who is affected, what Vercel already covers, and the self-hosted checklist.
Secure Server Actions in Next.js 16: Auth, Validation & Data Access LayerEvery Server Action is a public POST endpoint. Production pattern for Next.js 16: validate inputs with Zod, authenticate from session, authorize ownership, keep a thin action layer on a server-only Data Access Layer, constrain return values, and revalidate safely.
Next.js July 2026 Security Patch: Server Actions & Middleware Hardening GuideJuly 2026 Next.js security release: what changed, how to upgrade, Server Action allowedOrigins, middleware pitfalls, and a production App Router checklist after CVE-class fixes.
