Using nonces with Next.js #54907
Replies: 46 comments 61 replies
-
Known open issues: #49830 |
Beta Was this translation helpful? Give feedback.
-
Hi @leerob! I decided to add a CSP to my playground project just this week so I appreciate the timely update to the docs! The policy blocked multiple components and reported quite a few errors to the browser console which I tried to find a solution for with nonces. Unfortunately, I haven't had much success. I'm wondering if I've misunderstood something in my implementation? It's all contained within the additional 3 commits on this branch if you want to take a look. A couple extra bits I noticed while working on it that might be helpful feedback:
Also, #45184 is another relevant issue I believe which could potentially be closed by this? Thanks for working on this! |
Beta Was this translation helpful? Give feedback.
-
Hi! Two questions in the topic on using CSP with nonce before I submit any new issues
I've added middleware as in docs, following layout code works as expected (nonce's are added):
However when I remove the displaying of nonce (hence - nonce is not "consumed" in component) nonce value is no longer added to script tag. Why is that?
|
Beta Was this translation helpful? Give feedback.
-
@leerob Appreciate the new documentation page, however this only addresses server components (not client components). Many of the "Related" issues in the big list above are about problems with getting CSP nonces to work in client components, but you seem to have blanket replied and closed all of them with the link to the docs that only addresses the server component solutions. Hoping those issues can be re-opened... as it stands now it seems there is no way to have an effective CSP (that avoids using |
Beta Was this translation helpful? Give feedback.
-
Thanks for the updates and new documentation! The support for nonces looks like it will greatly improve my ability to implement a strict CSP. However, I've upgraded to 13.5 (and tried on canary) and I can't seem to get the nonce value to be added to any script tags. This even happens with a clean install of the |
Beta Was this translation helpful? Give feedback.
-
![]() I tried the latest version 13.5 with the example |
Beta Was this translation helpful? Give feedback.
-
@leerob Hi Lee - in theory I should be able to use "create-next-app" and scaffold a brand new app, then apply your middleware example for a CSP, and everything should "just work". However if I do exactly that I get the errors that @tszhong0411 shows above. Your example seems to work for adding third-party scripts but doesn't seem to work for basic Nextjs. I am sure I am missing something important. Would love to see a working example build off "create-next-app". |
Beta Was this translation helpful? Give feedback.
-
Hi, Since the nonce doesn't apply to a static page, what is the best approach in this case? Is there an alternative to using 'unsafe-inline'? Hash is a possibility for scripts I know beforehand, but next injects a bunch of scripts that are blocked unless we use 'unsafe-inline'. There is a better approach, because by using 'unsafe-inline' we are losing much of the security that CSP is supposed to provide. Thanks |
Beta Was this translation helpful? Give feedback.
-
Been fighting with CSP for a week now. Also on Nextjs "13.5.3". The issue I encountered is Agree with the others that a solution for static pages is highly appreciated! |
Beta Was this translation helpful? Give feedback.
-
@leerob, hi lee, is there any plan for next.js to implement hash based strict csp? nonce does not address the problem for static pages and it would be great if nextjs has a plan for this. |
Beta Was this translation helpful? Give feedback.
-
QQ: is there a way in Next.js (App Router) to allow CSP's One of the libraries I use in my Page component's code uses |
Beta Was this translation helpful? Give feedback.
-
For people looking to have a strict CSP on a static site, we've been using a postbuild script to rewrite the generated inline scripts into a separate chunk - #54152 (comment) |
Beta Was this translation helpful? Give feedback.
-
Hi @leerob, thank you for your nice documentation and examples. The error is away on production build When calling the at build time by the example generated error page by using a path, which is not specified, e.g. Could you please give some advice on updating the example, such that it runs with all its functions or is this not possible due to the statically generated sites? If so, is there a possibility to create the error pages as dynamic pages in the minimum example? |
Beta Was this translation helpful? Give feedback.
-
@leerob how about a case with NextJs app router and custom express server? I'm having a problem with CSP and I am not sure how I should tackle this. As soon as I add helmet to the express server it starts to block all scripts |
Beta Was this translation helpful? Give feedback.
-
From the documentation, I could not find how to get the nonce in I found out I can get the nonce from access const MyDocument = ({ props }: { props: { nonce: string } }) => {
const { nonce } = props
return (
<Html>
<Head nonce={nonce}>
<script
nonce={nonce}
async
src={`https://www.googletagmanager.com/gtm.js?id=${GTM_ID}`}
id="gtag-base"
/>
</Head>
<body>
<Main />
<NextScript nonce={nonce} />
</body>
</Html>
)
}
MyDocument.getInitialProps = async (ctx: DocumentContext) => {
const nonce = ctx.req ? ctx.req.headers["x-nonce"] : undefined
// This way only works for Windows. Apple and Mobile Devices does not work with this.
// const nonce = ctx.res?.getHeader("x-nonce")
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps, props: { nonce } }
} |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Is it possible to use nonce in Pages router (Next 13)? |
Beta Was this translation helpful? Give feedback.
-
So if I am using SSG with next.js v14.X.X, I need to give up on security with 'unsafe-inline'? |
Beta Was this translation helpful? Give feedback.
-
Any update for nonce in SSG? |
Beta Was this translation helpful? Give feedback.
-
I am experiencing issues setting up a strict Content Security Policy with the latest version of NextJS (currently 15.0.3).
Additionally, I've noticed that in development it's impossible to remove
It would be great to have some focus on the issue from the team, since CSP is quite an important aspect of security. |
Beta Was this translation helpful? Give feedback.
-
This is a huge problem for me as well. I’ve essentially been forced to make my entire app dynamic in order to keep it secure. This means I have to give up all of the following: ┌ ◐ / 11 kB 202 kB
├ ○ /_not-found 159 B 101 kB
├ ƒ /api/stripe/create-checkout-session 159 B 101 kB
├ ƒ /api/webhooks/clerk 159 B 101 kB
├ ƒ /api/webhooks/stripe 159 B 101 kB
├ ○ /blog 4.19 kB 123 kB
├ ○ /blog/[id] 188 B 110 kB
├ ○ /canceled 188 B 110 kB
├ ○ /polityka-prywatnosci 188 B 110 kB
├ ƒ /sign-in/[[...sign-in]] 723 B 130 kB
├ ƒ /sign-up/[[...sign-up]] 723 B 130 kB
├ ○ /success 6.26 kB 116 kB
├ ◐ /testy-opiekun 4.88 kB 154 kB
├ ○ /testy-opiekun/nauka 4.61 kB 114 kB
├ ○ /testy-opiekun/procedury 3.33 kB 122 kB
├ ○ /testy-opiekun/procedury/wyzwania 22.1 kB 128 kB
├ ○ /testy-opiekun/testy 4.36 kB 105 kB
├ ƒ /testy-opiekun/wyniki 5.02 kB 120 kB
├ ◐ /testy-opiekun/wyniki/[testId] 188 B 110 kB
├ └ /testy-opiekun/wyniki/[testId]
├ ○ /warunki 188 B 110 kB
└ ○ /wsparcie-projektu 736 B 116 kB
+ First Load JS shared by all 101 kB
├ chunks/3469eab5-8ce5f1d952c5d1ee.js 52.9 kB
├ chunks/4564-d6505d7cd6c5d89b.js 45.9 kB
└ other shared chunks (total) 1.92 kB Making everything dynamic could significantly increase my costs—especially since Vercel lambdas can be expensive. Beyond cost, this approach sacrifices the speed and performance benefits of static generation. Static pages are essential for delivering a fast, user-friendly experience, which is a priority for my app. Is the Next.js team planning any workarounds here? I’ve invested so much time forcing these new changes into my app, and now I have to rethink everything. |
Beta Was this translation helpful? Give feedback.
-
For v15.0.3. As i see it we have to enable csp only in production(npm run build && npm run start) and mark every page as async and have some async operation inside to avoid errors. If i am not mistaken, next/script doesn't require nonce, but what about next/image injecting width and height? Should those errors be ignored? I don't think it causes any problems though. To reproduce: import { headers } from "next/headers";
import Image from "next/image";
export default async function Home() {
const headersList = await headers();
const nonce = headersList.get('x-nonce') || "";
return (
<div>
<p className="red-box bg-green-500 size-20">
Hello World
</p>
<style nonce={nonce}>
{
`
.red-box {
background-color: red;
}
`
}
</style>
<Image
src={'./next.svg'}
alt="next logo"
width={100}
height={100}
/>
</div>
);
} import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
if (process.env.NODE_ENV === 'development') {
return NextResponse.next();
}
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'nonce-${nonce}';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`
// Replace newline characters and spaces
const contentSecurityPolicyHeaderValue = cspHeader
.replace(/\s{2,}/g, ' ')
.trim()
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
)
const response = NextResponse.next({
request: {
headers: requestHeaders,
},
})
response.headers.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
)
return response
}
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
} |
Beta Was this translation helpful? Give feedback.
-
Maybe there's hope #73891 :) |
Beta Was this translation helpful? Give feedback.
-
I stumbled upon this topic right now and I'm surprised that the CSP handling with Next.js is so cumbersome, even with v15. |
Beta Was this translation helpful? Give feedback.
-
@leerob However, there is one use specific case that I cannot get it to work. It is only in Production. For example, you have two pages, Page 1 and Page 2. We have a link to Page 2 in Page 1. If I access the Page 2 directly, the x-nonce header works well and it is in sync with everything. The script nonce will be set to the returned header and everything works like a charm. However, if I first access Page 1, and then click on the link to Page 2, a fetch request is made with an rsc parameter. A new header is returned, a new nonce is made by this fetch request, and it is used in the script for Page 2. A new header with CSP is also returned from it, as expected. However, I am facing the following situation: The first nonce in the image is the one used by the script in the Page 2. The one in the error is the one set in the initial request in the Page 1. I will keep investigating here, try to put on the head and meta params, and see where I can go. I am not updating the head or meta params yet. I thought that, by returning the header, the browser would understand. But it seems that the header returned by the fetch request that is made with the rsc parameter is being ignored, even though it is a new GET request. Or, should I make a context with a hook, load the nonce on the layout, and use that nonce set there for the scripts, so this script will use the nonce set up by the initial request (is there any security problem by doing this?). In this case it is a script loaded in a client component (context will probably work), so I would not know how to make it work for server components, since contexts are not usable there. My current knowledge is that the nonce should be returned inside the Content-Security-Policy header in each request, and that is happening. Here is an image with the fetch request with rsc returning the header. It is also returned in the initial request, but the nonces are different. Don't worry about the URL, it is only making alias and for cookie related stuff. Even on staging environment it does not work as well. If I have a solution for this I will post here to help everyone. If someone has any idea how to fix this, I appreciate. I am thinking on stopping the nonce for this and using domain CSP if I cannot make it work. Sorry for the long post. Thank you in advance. |
Beta Was this translation helpful? Give feedback.
-
I think this needs to be revisited. If you want to use a secure csp with next.js, you must explicitly rely on dynamic rendering across the entire app. I understand this is due to how scripts are loaded, but i dont know if this is a fundamental limitation or not. |
Beta Was this translation helpful? Give feedback.
-
I'm still looking for a solution for our static website. Right now we have an NGINX application in front of our site that replaces a secretNonce string in our _document.js with a newly generated string every request and also adds a csp header. As I see it, it shouldn't be too hard for some nextjs middleware to do the same, eg to replace a string in the statically generated pages with a new string on every request, and have every request with a new csp-header w that new nonce, but keep pages still generated statically. Can some nextjs wizard please implement this? 😊 Or maybe explain why this isn't possible? :) |
Beta Was this translation helpful? Give feedback.
-
Hey guys, we are experiencing the same challenge here regarding having a |
Beta Was this translation helpful? Give feedback.
-
Hi, can someone help me understand if there is another flow of rendering if you specifically set up the I'm asking because I have a strange case when the style node renders with that specific header - I have the hydration error for the nonce attribute. Example MR |
Beta Was this translation helpful? Give feedback.
-
This is normal, u need to set suppressHydrationWarning on all the elements. The nonce is removed for security reasons.Am 18.04.2025 12:58 schrieb Ivan Reutenko ***@***.***>:
Hi, can someone help me understand if there is another flow of rendering if you specifically set up the Content-Security-Policy response header?
I'm asking because I have a strange case when the style node renders with that specific header - I have the hydration error for the nonce attribute. Example MR
image.png (view on web)
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
After digging through many different issues and discussions, I've made a new page in the documentation (PR) specifically for Content Security Policy and nonces. This docs page:
nonce
with Middlewarenonce
in a route withheaders()
unsafe
nonce
Middleware from running on prefetches / static assetsFurther, we've patched some bugs and made improvements to
nonce
handling in Next.js that will be available in the latestcanary
version (for those of you time traveling from the future, upgrade to Next.js 13.5). We also updated thewith-strict-csp
example in theexamples/
folder, which is backlinked from the new documentation page.This discussion is for following up on the latest
nonce
updates and CSP documentation to continue the discussion, and combining many separate discussions into here.Related
nonce
isn't applied for Content-Security-Policy #21925Beta Was this translation helpful? Give feedback.
All reactions