Skip to content

fix(#757): inject nonce on streamed body chunks (render:html:chunk) - #758

Closed
nguyengg wants to merge 1 commit into
Baroshem:mainfrom
nguyengg:fix/nonce-ssr-streaming-chunk
Closed

nguyengg wants to merge 1 commit into
Baroshem:mainfrom
nguyengg:fix/nonce-ssr-streaming-chunk

Conversation

@nguyengg

Copy link
Copy Markdown

Types of changes

  • Bug fix (a non-breaking change which fixes an issue)

Description

Fixes #757.

When experimental.ssrStreaming: true (Nuxt 4.5+), the body / bodyAppend sections are flushed to the wire before the render:html hook can mutate them, so scripts injected there — notably window.__NUXT__ config and <script id="__NUXT_DATA__"> — reach the browser without a nonce and are blocked by a strict CSP, breaking hydration entirely (blank page, TypeError in console).

Fix

src/runtime/nitro/plugins/40-cspSsrNonce.ts gains a render:html:chunk hook alongside the existing render:html hook:

  • render:html continues to handle head / bodyPrepend (which still work in streaming) plus the whole document in buffered mode — unchanged.
  • render:html:chunk fires once per streamed chunk and patches script / style / link tags on their way out.

The chunk hook uses a negative-lookahead regex (<script(?![^>]*\snonce=)...) so tags already noncified via render:html are not re-stamped when they appear in a streamed chunk. In buffered mode the new hook simply never fires — non-streaming behavior is unchanged.

The 'render:html:chunk' as 'render:html' cast on the hook name is because the hook is only declared in Nitro's NitroRuntimeHooks on newer versions; the cast is a no-op at runtime and lets the module compile against the currently-pinned Nitro without a type augmentation.

Verification

  • yarn lint — clean.
  • npx vitest run test/ssrNonce.test.ts — 7 pass, 1 skipped (baseline unchanged; no regression to the buffered code path).

Testing note

The repo currently pins nuxt@^4.2.1, which predates experimental.ssrStreaming. A full integration test that boots a fixture with experimental.ssrStreaming: true and asserts every <script> in the response carries a nonce would need a devDep bump to Nuxt ≥ 4.5. Happy to submit that as a follow-up PR if the maintainers would like it — I kept this PR narrowly scoped to the runtime fix so it can be reviewed and released quickly.

The regex + Buffer conversion in the new hook mirrors the workaround the reporter validated in production (see the issue's "Workaround" block), just moved inside the module so consumers don't have to write a server plugin.

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes (if not applicable, please state why)

Existing ssrNonce.test.ts still passes (verifies buffered-mode behavior is untouched). A streaming-mode integration test requires bumping the pinned nuxt devDep to ≥ 4.5; deferred to a follow-up so this PR stays surgical.

When `experimental.ssrStreaming: true` (Nuxt 4.5+), the body / bodyAppend
sections are flushed to the wire before the `render:html` hook can mutate
them, so scripts injected there (notably `window.__NUXT__` and
`__NUXT_DATA__`) reach the browser without a nonce and are blocked by a
strict CSP, breaking hydration.

Add a `render:html:chunk` hook alongside the existing `render:html` hook.
The new hook patches script/style/link tags on their way out via a
negative-lookahead regex, so tags already noncified via `render:html`
(head / bodyPrepend, which still work in streaming mode) are not
re-stamped. In buffered mode the hook simply never fires, so
non-streaming behavior is unchanged.

Fixes #757
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

@nguyengg is attempting to deploy a commit to the Baroshem's projects Team on Vercel.

A member of the Team first needs to authorize it.

@nguyengg

Copy link
Copy Markdown
Author

Closing at author's request — will revisit later.

@nguyengg nguyengg closed this Aug 26, 2026
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.

Nonce not injected on bodyAppend scripts when experimental.ssrStreaming is enabled

1 participant