[App Router] Content Security Policy Broken
Link to the code that reproduces this issue
https://github.com/moloch--/nextjs-broken-csp
To Reproduce
- Follow directions at https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy
- Add
unsafe-evalto CSP as it's required, though the documentation doesn't include it npm run buildandnpm run start- Page fails to load, the CSP response headers are present but the nonce is not rendered into the DOM
Notably, the nonces are rendered in the DOM when using npm run dev but only fail to render properly in production builds.
Current vs. Expected behavior
Current
Content-security-policy requires unsafe-eval contrary to the offically documented examples, and nonces are not rendered in production builds.
Expected
Content-security-policy can be used without any unsafe content sources, and works with production builds.
Provide environment information
Next.js v14.1.1
Node v20.x - v21.x
Docker `public.ecr.aws/docker/library/node:20-slim`
Which area(s) are affected? (Select all that apply)
App Router
Which stage(s) are affected? (Select all that apply)
next start (local)
Additional context
CSP without unsafe-eval
Docs' Example CSP in Production Build (Nonces)
Can confirm. 14.0.4 still works fine for me.
@moloch--
I am not quite sure, since I have not used CSP in my hobby or in work coding, but document says you must use dynamic rendering to add nonces. , and I think app-router is default to cache.
So I think you have to add
export const dynamic = "force-dynamic"
or something to prevent next.js from cacheing in page.tsx in your reproduction codes.
In my case, adding above code fix console.log to warn.
Doesn't seem like I should have to force dynamic rendering for every page just to use CSP, so still seems like a bug?
@AlexBueckig would you be able to link to a working example?
Adding force-dynamic solved the problem for me. I totally makes sense, now that I'm thinking about it. A new nonce gets created for every request by your middleware function. If you cache a rendered page the nonces in this page are a value from a previous request. But since you create a new csp header for every request, there is a mismatch with these nonces and csp breaks.
For now this seems to be the only option if you want to use nonces.
I don't know your project, but if you aren't using external scripts e.g. tracking or whatever you can just set static csp headers without the need for strict-dynamic and nonces. These versions should be cacheable since they are not dependant on a random generated value.
Experienced this issue again, back on 14.1.3. force-dynamic is activated and no other CSP-Errors occur.
Here are some screenshots from the developer tools showing the error and file:
btw, adding export const dynamic = "force-dynamic" doesn't seem to have any affect for me :-/
Okay for anyone else that sees this, I figured it out. I had "use client" in my layout.tsx which means the nonces are never inserted even if you use force-dynamic
"unsafe-eval, unsafe-inline" as a csp header just doesn't look right. It's basically a false sense of security.
The problem is coming from the inline script tags, which is generated by Next.js framework on whenever I build project and run it (as opposed to running dev mode). Since I cannot add nonce to a code that I myself did not write, I tried hash method. I found all the hash of the inline scripts. Yet it still fails because everytime I rebuild, there's one more inline script with a new hash value.
@backdevjung I’m experiencing the same issue: Next.js is generating multiple
As a suggestion, Next.js should at least add integrity attributes to the