Skip to content

fix: apply CSP nonce to Pages Router streamed Suspense scripts - #97851

Open
mataspetrikas wants to merge 1 commit into
vercel:canaryfrom
mataspetrikas:fix/pages-router-nonce-streamed-suspense-scripts
Open

fix: apply CSP nonce to Pages Router streamed Suspense scripts#97851
mataspetrikas wants to merge 1 commit into
vercel:canaryfrom
mataspetrikas:fix/pages-router-nonce-streamed-suspense-scripts

Conversation

@mataspetrikas

@mataspetrikas mataspetrikas commented Aug 25, 2026

Copy link
Copy Markdown

Hi NextJs team,

What?

I am working on a large pages-router-based NextJS app and while implementing CSP headers, I have discovered an annoying bug. It seems that nonce is not passed to the suspense boundaries when using Pages Router. The App router seems to be unaffected. The bug was already previously reported in teh past eg #96443

Why?

We could see that the Pages router correctly gets the nonce from the header and applies it to the script tags it renders. But it doesn't hand it over to React Fizz renderer. So the scripts in boundaries get blocked when enfiorcing the CSP policy. An example of a blocked script would be the Reacts reveal timing script requestAnimationFrame(function(){$RT=performance.now()}); that gets inlined by React 19.x.

How?

We tried different approaches to fix that but we either had to rewrite the script tags in the server or monkey-patch nextJS. Using Claude we could narrow down the problem to one line in the render.tsx - simply by passing the nonce to the Fizz it would fix the issue.

Tests added that should show how the suspend then works after CSP fix - it fails without a fix, correctly verifying the problem.

Pls let me know if you need any chanegs or additional info.

Fixes #96443

The Pages Router already derives a nonce from the request's
Content-Security-Policy header and applies it to the script tags it
renders, but it never forwards that nonce to Fizz. React therefore emits
its Suspense reveal instructions (the $RB/$RC/$RS family) as bare
<script> tags, which a nonce-based CSP blocks.

renderToInitialFizzStream already accepts streamOptions and passes them
straight through to renderToReadableStream, and React honours a nonce
there, so the shell render just needs to pass the nonce it already has.

Fixes vercel#96443
@mataspetrikas
mataspetrikas marked this pull request as ready for review August 25, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Content-Security-Policy nonce is not applied to streamed Suspense boundary scripts in Pages Router SSR

1 participant