Repository navigation
CSP SRI (hashes) and nonce for ISR pages and custom <Script> tags in <head> of root layout. #95678
Replies: 5 comments
|
I don't think you can keep a per-request nonce and keep ISR/static at the same time. A nonce has to be unique per request, and Next only injects it during server-side rendering, so using one forces every page to render dynamically. The experimental Your analytics One potential bug in your setup: your production CSP is So for the static/ISR path, drop // next.config.mjs
const cspHeader = `
default-src 'self';
script-src 'self' https://ghostlyx.com;
style-src 'self';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`
// keep your existing experimental.sri configThis keeps ISR: Next's own scripts pass via btw in your |
|
You're mixing two CSP strategies that have different trade-offs, and unfortunately they don't complement each other in the way we'd like. The experimental A nonce-based CSP, on the other hand, is inherently request-specific. Every request gets a new random nonce, which means the page has to be rendered dynamically so the nonce can be injected into both the CSP header and the corresponding For your analytics script specifically, you have a few options:
Since your script is external: <script src="https://ghostlyx.com/js/script.js"></script>you can simply allow that origin in This lets you keep ISR/static rendering without introducing nonces.
Hashes are intended for inline scripts with known content. They don't help for an external script whose contents may change over time unless you manage the hash yourself.
If the analytics provider requires an inline bootstrap script or you need to authorize dynamically generated scripts, then a nonce is the correct solution. In that case, you'll have to accept dynamic rendering for those pages. One thing I noticed in your example is that you're generating a nonce only when
Out of curiosity, does |
|
You can keep ISR, keep SRI, and still load the analytics script, and the trick is that you do not need the nonce at all. The key is how Under CSP3, So concretely: keep One thing in your draft to be aware of: with The combination you asked about literally (nonce plus hashes in the same policy) is technically allowed by the spec, both are valid sources, but it buys you nothing here because generating the nonce is what forces dynamic rendering. Once you realize the analytics script can ride on |
|
The short answer is that there isn't a way to combine per-request nonces with static/ISR rendering in the way you're hoping. A CSP nonce is generated for every request and must appear both in the CSP header and the corresponding <script> tags. Since static and ISR pages are produced ahead of time, Next.js cannot inject a fresh nonce into them, which is why enabling nonce-based CSP forces dynamic rendering. If your goal is to preserve ISR/static rendering, the recommended approach is:
One thing I also noticed is that your production policy currently contains: script-src 'self' 'strict-dynamic' https://ghostlyx.com If the analytics script is parser-inserted (for example If the script is loaded after hydration via So the practical options are: • Static/ISR → SRI + no nonce. Unfortunately this isn't a Next.js bug so much as a limitation of how CSP nonces work. The Next.js documentation separates SRI and nonce-based CSP because they're intended for different rendering strategies. |
|
Taking the literal question first: yes, the guide says they combine: "For dynamic rendering scenarios, you can still generate nonces with proxy if needed, combining both SRI integrity attributes and nonce-based CSP approaches", but only on dynamically rendered pages, which is the thing you're trying to avoid. On the static/ISR path 1. SRI puts no hash into your policy. It only attaches 2. Dropping // app-render/use-flight-response.tsx
const startScriptTag = nonce
? `<script nonce="${htmlEscapeAttributeString(nonce)}">`
: '<script>'
Pinning hashes by hand doesn't rescue it either, because the inline payload changes per page and per build, and Next's own SRI test notes that some flight-loaded chunks in the initial HTML get no So today it's If you self-host behind a proxy you control there's a third path, though it isn't a Next recipe and I've only run it on nginx in front of a static site: render a constant sentinel where the nonce goes, cache that, and mint a fresh value per response at the edge, rewriting body and CSP header together and marking the result |
Uh oh!
There was an error while loading. Please reload this page.
Summary
Hi,
I'm looking into implementing the CSP (Content-Security-Policy) for my webapp.
I followed this amazing guide at https://nextjs.org/docs/app/guides/content-security-policy
I'd like to use the experimental SRI for specifying the hash for scripts, however I have a <Script> in my root layout.tsx which requires a nonce.
Is it possible to combine the SRI hash CSP with a nonce based CSP?
I'd much prefer to keep static or ISR pages over pages rendered on the server dynamically every time, for performance reasons.
This is simply an analytics Script so it's not entirely necessary but it would be great if it's possible to keep the advantages of SRI and ISR over all dynamic pages simply for an analytics script.
Any suggestions on how I'd go about implementing this?
Here's my first attempt:
SRI in next.config.mjs
And extract from my proxy.ts file:
I'm not sure how to resolve the conflict between these two and enable both SRI hash CSP and nonce-based CSP specifically for this script in root layout.tsx:
Any help is appreciated!
Additional information
No response
Example
No response
All reactions