Skip to content
MDView as Markdown

Next.js

Next.js has the most complete CSP story of any framework here, and also the most honest one: it documents not just how to add a nonce, but what adopting one costs you. That trade-off is the thing to understand before you start.

What Next.js gives you

  • Nonce generation in the proxy layer (proxy.ts, previously middleware), set on both the Content-Security-Policy header and a custom x-nonce header.
  • Automatic nonce application during server-side rendering. Next.js parses the CSP header, extracts the 'nonce-{value}' pattern, and attaches it to framework scripts, page bundles, its own inline styles and scripts, and any <Script> component - you do not tag them by hand.
  • headers() in a Server Component for reading the nonce back, e.g. to pass to <GoogleTagManager nonce={nonce} />.
  • A no-nonce route: set the header statically in next.config.js via the headers() config.
  • Experimental hash-based CSP via Subresource Integrity, which keeps static generation intact.

Setting the header

Report-only is the safe starting point. Your URLs come from the Setup page:

const cspHeader = `
  default-src 'self';
  script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'report-sha256';
  object-src 'none';
  base-uri 'self';
  report-uri https://abc123.report-uri.com/r/d/csp/reportOnly;
  report-to default;
`
response.headers.set(
  'Content-Security-Policy-Report-Only',
  cspHeader.replace(/\s{2,}/g, ' ').trim(),
)
response.headers.set('Reporting-Endpoints', 'default="https://abc123.report-uri.com/a/d/g"')
response.headers.set('Report-To', JSON.stringify({
  group: 'default',
  max_age: 31536000,
  endpoints: [{ url: 'https://abc123.report-uri.com/a/d/g' }],
  include_subdomains: true,
}))

The Reporting-Endpoints and Report-To headers tell the browser where report-to default sends reports, including the CSP Integrity reports that 'report-sha256' turns on.

Filter the proxy with a matcher so it skips API routes, _next/static, _next/image and prefetches - those do not need the header and generating a nonce for them is wasted work.

Things to watch for

  • Nonces force dynamic rendering. Static optimisation and ISR are disabled, pages cannot be CDN-cached by default, and Partial Prerendering is incompatible because the static shell has no access to the nonce. This is a real architectural cost, not a footnote.
  • 'unsafe-eval' is required in development because React uses eval for better error stacks. It is not required in production - gate it on NODE_ENV rather than shipping it.
  • If you need to keep static generation, the experimental SRI support is the alternative: hashes are generated at build time and added as integrity attributes, so pages stay cacheable.
  • A <meta> tag policy cannot carry report-uri or report-to. If you want reports, the policy has to be a header.

Official documentation

Next.js - How to set a Content Security Policy (CSP) for your Next.js application

Start Monitoring with Report URI

Already using us for CSP? Find your CSP reporting directives on Setup, or view your CSP reports.

New to Report URI? Create an account and grab your CSP reporting directives from the Setup page.

Start your free trial