
- Nonces fit server apps.
- A CSP hash matches content, preserving static edge CDN caching.
- Pair nonces with strict-dynamic, and put JSON state into data blocks.
When choosing between a CSP nonce vs hash for inline scripts, your hosting setup decides the winner. A Content Security Policy nonce gives each HTTP response a fresh random token. A hash matches script code. Use a hash if your website relies on static site generators or full-page CDN caching. Choose a nonce if your site renders pages dynamically on an application server or edge worker. The wrong choice will break caching or complicate builds.
Dropping unsafe-inline is a crucial security step. Both nonces and hashes stop script injection by ignoring untrusted tags. They protect users well. The core challenge is making these rules work across live releases without slowing down page delivery.
CSP Nonce vs Hash: The Core Architectural Difference
The main difference between these two tools comes down to where state lives. A nonce lives directly in the HTTP network response header. Your server creates it anew on every hit. A hash lives in the static asset bytes. You calculate it from the exact script text before shipping files to users.
A nonce must stay unique on every visit. Because of this, web proxies cannot cache the final HTML page for multiple visitors. Token reuse is risky. If an edge proxy caches the page with its nonce, the secret token becomes public. An attacker can inspect the cached page, grab the token, and inject malicious scripts. This means nonces require dynamic page rendering or edge workers that rewrite response headers and tags.
A hash needs no server logic. You compute the digest during your build, add the string to your web server headers, and deploy static files. A CDN can cache these files across global edge nodes. One edit breaks it. If an engineer edits one space or changes a semicolon, the hash changes at once. If the header does not update at the same time, the browser blocks the script.
How CSP Nonces Work: Runtime Tokens and Edge SSR
A nonce is a number used once. Under the Content Security Policy Level 3 standard, a nonce must hold at least 128 bits of cryptographic entropy. It must use base64 encoding. Every response must generate a distinct value.
Content-Security-Policy: script-src 'nonce-rAnd0m128b1tStr1ng' 'strict-dynamic';
Your application template inserts the matching value into each trusted script tag.
<script nonce="rAnd0m128b1tStr1ng">
window.__APP_CONFIG__ = { apiBase: "https://api.example.com" };
</script>
When the browser loads the page, it checks the attribute against the header. If the two strings match, the script runs. Under CSP Level 3, the browser hides the nonce attribute from the DOM. This stops untrusted scripts from reading the token through script queries.
If your app loads external scripts dynamically, add strict-dynamic to your policy. When this keyword is present, any script approved by the nonce can create and run new script tags. Trust cascades down. The browser trusts the execution chain, so you do not need long domain allowlists for tag managers or code bundles.
The starting Content Security Policy header guide shows how to test headers in report-only mode first. Run that staged check before you enforce nonces on production traffic.
How CSP Hashes Work: Build-Time Digests and Static Sites
A hash policy calculates a digest from the exact code between script tags. You can use SHA-256, SHA-384, or SHA-512 algorithms. The resulting binary digest is base64-encoded and prefixed with the algorithm name.
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZPDBV5tE=';
You can compute this digest with standard command-line tools during your build.
echo -n 'console.log("analytics initialized");' | openssl dgst -sha256 -binary | openssl base64 -A
Whitespace matters when using hashes. If your build minifier drops a trailing newline or swaps quotes, the hash breaks. For static sites, automate digest generation as a post-build step. Have your script scan built HTML files and update your hosting headers.
Hashes in script-src do not apply to external script files. For external files, use Subresource Integrity for third-party scripts by adding an integrity attribute directly to your script tag.
Decision Matrix: Nonce vs Hash Across Common Stacks
Choosing between a nonce and a hash depends on how you build and host your site. Check this summary first.
| Factor | CSP Nonce | CSP Hash |
|---|---|---|
| Generation Point | Runtime per request (server or edge) | Build-time calculation (compiler or CLI) |
| CDN Edge Caching | Breaks full-page static caching | Fully compatible with edge caching |
| Maintenance Overhead | Zero header edits on script code updates | Must recompute digest on any byte change |
| Dynamic Script Loading | Propagates trust with strict-dynamic | Does not propagate trust to child scripts |
| Best Architectural Fit | Node, Rails, Django, Cloudflare Workers SSR | Static sites, SSG, Astro, Hugo, S3, Jamstack |
Pair this policy with a strong Permissions Policy to constrain third-party frames. You can also add Fetch Metadata request headers to protect your application boundaries.
The Hidden Trap: Dynamic State and Hydration Scripts
Dynamic data causes traps. A common mistake with CSP hashes is trying to hash runtime state. Modern web frameworks often print server state into an inline script to hydrate client components. User IDs, timestamps, and session flags change on every visit. A static hash breaks.
Do not switch to a nonce just to pass state on a static page. Change the container tag instead. Browsers only execute code in script tags that lack a type attribute or use JavaScript MIME types. If you place your data in a non-executable block, CSP script-src ignores it.
<script type="application/json" id="server-state">
{"userId": 4821, "features": ["darkMode", "betaSearch"]}
</script>
Your static JavaScript code can read this state safely with document.getElementById and JSON.parse. This pattern keeps pages fast and CDN-friendly without weakening script rules.
Frequently Asked Questions About CSP Nonces and Hashes
Can a CSP hash authorize inline event handlers?
Standard script-src hashes only authorize inline script elements. Inline event handlers like onclick require the unsafe-hashes directive in Content Security Policy Level 3, which weakens script boundaries and is not recommended.
Why does a CSP nonce break static CDN caching?
A nonce must be unique on every HTTP response. If a CDN caches and serves the same HTML document to multiple visitors, attackers can reuse that cached nonce value to bypass injection protections.
Does strict-dynamic work with both nonces and hashes?
The strict-dynamic directive works with both, but only nonces allow trust propagation to dynamically generated script elements. A hash authorizes only the exact matched script content and cannot trust child scripts.
Sources
W3C: Content Security Policy Level 3 Specification · MDN Web Docs: script-src Directive Reference · OWASP: Content Security Policy Cheat Sheet. Consult official browser platform documentation before deploying headers to production.
More in this sectionTechnology →