Skip to content

fix(nextjs): forward CSP nonce as request header in clerkMiddleware#7828

Merged
jacekradko merged 2 commits intomainfrom
jacek/fix-csp-nonce-request-headers
Feb 13, 2026
Merged

fix(nextjs): forward CSP nonce as request header in clerkMiddleware#7828
jacekradko merged 2 commits intomainfrom
jacek/fix-csp-nonce-request-headers

Conversation

@jacekradko
Copy link
Member

@jacekradko jacekradko commented Feb 12, 2026

Summary

  • Forward CSP headers (including x-nonce) as request headers via setRequestHeadersOnNextResponse in clerkMiddleware, so headers() in server components can read the nonce
  • Update the integration template middleware to use the built-in contentSecurityPolicy: { strict: true } option instead of a manual CSP string
  • Add unit tests verifying CSP headers are forwarded as request headers in both strict and non-strict modes

Problem

When using clerkMiddleware({ contentSecurityPolicy: { strict: true } }), the generated nonce was only set as a response header (x-nonce, content-security-policy). It was never forwarded as a request header via the x-middleware-override-headers mechanism. This meant:

  • headers() in server components couldn't read the nonce
  • ClerkProvider's getNonceHeaders() returned ''
  • Next.js couldn't apply the nonce to its <script> tags
  • With strict-dynamic, browsers ignore 'self', so unnonced scripts got blocked

The manual approach (setting CSP on req.headers) worked because decorateRequest copies request headers. The built-in option bypassed this path.

Test plan

  • pnpm build passes
  • pnpm test in packages/nextjs — all 108 tests pass (including 2 new CSP forwarding tests)
  • git diff main --stat shows only intended files

Summary by CodeRabbit

  • Bug Fixes

    • Fixed CSP nonce forwarding to request headers, enabling server components to access the nonce and properly apply it to script tags when using strict CSP mode.
  • Tests

    • Added tests verifying CSP header forwarding behavior in strict and standard modes.

When using `clerkMiddleware({ contentSecurityPolicy: { strict: true } })`,
the generated nonce was only set as a response header but never forwarded
as a request header via `x-middleware-override-headers`. This meant
`headers()` in server components couldn't read the nonce, so
`ClerkProvider`'s `getNonceHeaders()` returned empty and Next.js couldn't
apply the nonce to its `<script>` tags — breaking `strict-dynamic` CSP.

Forward CSP headers (including `x-nonce`) as request headers using
`setRequestHeadersOnNextResponse` so server components can access them.
Also update the integration template to use the built-in CSP option.
@vercel
Copy link

vercel bot commented Feb 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Feb 12, 2026 4:04pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Feb 12, 2026

🦋 Changeset detected

Latest commit: acf04f8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@clerk/nextjs Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@jacekradko
Copy link
Member Author

!snapshot

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 12, 2026

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7828

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7828

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7828

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7828

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7828

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7828

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7828

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7828

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7828

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7828

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7828

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7828

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7828

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7828

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7828

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7828

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7828

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7828

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7828

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7828

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7828

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7828

commit: acf04f8

@clerk-cookie
Copy link
Collaborator

Hey @jacekradko - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.3.0-snapshot.v20260212160100
@clerk/astro 3.0.0-snapshot.v20260212160100
@clerk/backend 3.0.0-snapshot.v20260212160100
@clerk/chrome-extension 3.0.0-snapshot.v20260212160100
@clerk/clerk-js 6.0.0-snapshot.v20260212160100
@clerk/dev-cli 1.0.0-snapshot.v20260212160100
@clerk/expo 3.0.0-snapshot.v20260212160100
@clerk/expo-passkeys 1.0.0-snapshot.v20260212160100
@clerk/express 2.0.0-snapshot.v20260212160100
@clerk/fastify 2.7.0-snapshot.v20260212160100
@clerk/localizations 4.0.0-snapshot.v20260212160100
@clerk/msw 0.0.1-snapshot.v20260212160100
@clerk/nextjs 7.0.0-snapshot.v20260212160100
@clerk/nuxt 2.0.0-snapshot.v20260212160100
@clerk/react 6.0.0-snapshot.v20260212160100
@clerk/react-router 3.0.0-snapshot.v20260212160100
@clerk/shared 4.0.0-snapshot.v20260212160100
@clerk/tanstack-react-start 1.0.0-snapshot.v20260212160100
@clerk/testing 2.0.0-snapshot.v20260212160100
@clerk/ui 1.0.0-snapshot.v20260212160100
@clerk/upgrade 2.0.0-snapshot.v20260212160100
@clerk/vue 2.0.0-snapshot.v20260212160100

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.3.0-snapshot.v20260212160100 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20260212160100 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20260212160100 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20260212160100 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20260212160100 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20260212160100 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20260212160100 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260212160100 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20260212160100 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.7.0-snapshot.v20260212160100 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20260212160100 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.1-snapshot.v20260212160100 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20260212160100 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20260212160100 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20260212160100 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20260212160100 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20260212160100 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260212160100 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20260212160100 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20260212160100 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20260212160100 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20260212160100 --save-exact

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

The changes implement CSP nonce forwarding in the Clerk Next.js middleware. A changeset is added for a patch release. The next-app-router template middleware is updated to pass contentSecurityPolicy: { strict: true } as an option to clerkMiddleware. The clerkMiddleware implementation is modified to forward CSP headers as request headers via setRequestHeadersOnNextResponse. Two new tests verify CSP header handling in strict and non-strict modes, confirming that nonce headers are properly set and forwarded through middleware override headers.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: forwarding CSP nonce as request headers in clerkMiddleware, which is the core fix across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


No actionable comments were generated in the recent review. 🎉


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Member

@wobsoriano wobsoriano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks clean

@jacekradko jacekradko merged commit 145252e into main Feb 13, 2026
41 checks passed
@jacekradko jacekradko deleted the jacek/fix-csp-nonce-request-headers branch February 13, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants