Headline: A Content Security Policy (CSP) is an HTTP response header that tells the browser which script, style, and connection sources a document is allowed to use, and in the Next.js App Router the only script policy that survives the framework's runtime chunk loading is a per-request nonce combined with 'strict-dynamic'. The cost I did not budget for: generating that nonce in middleware.ts and reading it with headers() opts every matched route out of static rendering.

Key takeaways

A CSP nonce is a per-request random token that appears both in the script-src directive and as a nonce attribute on every allowed <script>. Because it must never repeat, HTML carrying a nonce cannot be cached — which is exactly why Next.js drops the route to dynamic rendering.

A host allowlist cannot secure a Next.js app. The App Router emits an inline bootstrap payload (self.__next_f.push(...)) and then creates further <script> elements at runtime, so script-src 'self' both fails to allow the inline payload and fails to distinguish framework chunks from any other same-origin file.

'strict-dynamic' propagates trust from a nonce-allowed script to any script element that script creates programmatically. It is what makes chunk loading work without enumerating chunk URLs.