Skip to main content
Security·11 min read

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.