Skip to content

CSP request header stripped on Cloudflare — Next.js automatic script nonce doesn't work #1302

Description

@clarkyu

Summary

Next.js's automatic script nonce reads the nonce from the content-security-policy request header in app-render (parseRequestHeaders). Setting it from middleware via NextResponse.next({ request: { headers } }) works under next dev (every script gets the nonce) but not on @opennextjs/cloudflare.

Symptom

Same code, same middleware:

Env page <script> with nonce
next dev 30 30 / 30
production (@opennextjs/cloudflare) 19 1 / 19 (only an app inline script that reads a custom x-nonce header)

So a strict script-src 'self' 'nonce-…' 'strict-dynamic' policy can't be enforced — every framework script would be blocked.

Root cause (traced end to end)

  1. Middleware runs as a separate function; the forwarded Request is rebuilt by the edge converter — overrides/converters/edge.js convertTo, new Request(url, { headers }). Cloudflare's workerd strips content-security-policy (and content-security-policy-report-only) request headers at this reconstruction. Custom headers (e.g. x-nonce) survive.
  2. Restoring content-security-policy from a surviving header inside the converter's convertFrom (on the plain-object headers) does not help either — it is stripped again before Next's app-render reads it (verified by encoding the restored value into the emitted nonce and observing it never reaches the HTML in production).
  3. Node/undici's new Request() keeps these headers, so this is workerd-specific, not a Fetch-spec forbidden request header.

Reproduce

  • Middleware sets a per-request nonce and requestHeaders.set('content-security-policy', "script-src 'nonce-'+nonce+' 'strict-dynamic'"), returns NextResponse.next({ request: { headers: requestHeaders } }).
  • Render any App-Router page; inspect the HTML in production vs next dev: framework <script> tags lack the nonce in production.

Versions

@opennextjs/cloudflare 1.20.1, @opennextjs/aws 4.0.2, next 16.2.9, wrangler 4.106, compatibility_date 2024-12-30, nodejs_compat.

Suggested fix

Carry the content-security-policy request header through to the server render in a workerd-safe way (e.g. restore it after the final Request reconstruction, or expose the middleware nonce to the server render in a channel Next can read), so Next's automatic nonce works on Cloudflare as it does under next dev.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions