Hashes and nonces
How CSP nonce and hash sources allow specific inline scripts and styles without unsafe-inline, with the rules for each.
Last update:
Nonce and hash sources let a Content Security Policy (CSP) allow a specific inline
script or style without turning on
'unsafe-inline'.
A nonce is a one-time token shared between the policy and the element; a hash is a
digest of the element's exact content. Both say "trust this particular inline
code and nothing else", which is the foundation of a strict CSP.
A nonce policy and the script tag that matches it:
Content-Security-Policy: script-src 'nonce-{RANDOM}'<script nonce="{RANDOM}">init();</script>Syntax
A nonce source is 'nonce-' followed by a base64 value. A hash source is an
algorithm label, sha256, sha384, or sha512, a hyphen, and the base64 digest.
Content-Security-Policy: script-src 'nonce-r4nd0m' 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc='The matching element references the same nonce, or simply has content whose digest equals the hash.
<script nonce="r4nd0m">doSomething();</script>| Value | Status | Description |
|---|---|---|
Nonce source 'nonce-<base64>' | ✅ Good | A fresh, unguessable per-response token matching the element's nonce attribute. |
| Hash source for inline content | ✅ Good | A sha256, sha384, or sha512 digest of the exact inline script or style text. |
| Hash source for external scripts | ✅ Good | Matches the script's SRI-style integrity digest. Widely supported across current browsers. |
Hash for event handlers, with 'unsafe-hashes' | ❌ Risky | Widens hash matching to inline handlers and style= attributes, re-opening that surface. |
When a nonce or hash is present in a directive,
'unsafe-inline'
in that same directive is ignored. Once you add a nonce or a hash, remove
'unsafe-inline': the browser already ignores it, so it does nothing but clutter
the policy.
Nonce rules
A nonce only protects you if an attacker cannot predict or reuse it. Follow all of these.
- Generate it on the server, fresh for every response. A static nonce baked into a
template is equivalent to
'unsafe-inline', because an injected script can copy the known value. - Use a cryptographically secure random generator (CSPRNG) with at least 128 bits of entropy, encoded as base64.
- Make it unique per response. Never cache a page with its nonce, and never reuse one across requests.
- Put the same value in the policy and in the element's
nonceattribute. A mismatch blocks the script.
Content-Security-Policy: script-src 'nonce-8IBTHwOdqNKAWeKl7plt8g=='<script nonce="8IBTHwOdqNKAWeKl7plt8g==">init();</script>See setting up a CSP nonce for a per-framework walkthrough.
Hash rules
A hash source matches an inline element whose content hashes to the given digest. It needs no per-response token, which makes it a good fit for static inline code.
- The digest is computed over the exact text content of the inline
<script>or<style>: the UTF-8 bytes between the tags, with every space, newline, and character counted, and no surrounding tags. A single whitespace change invalidates the hash. - Use
sha256,sha384, orsha512. The algorithm in the source must match the one you computed. - To match an inline event handler (such as
onclick=) or astyle=attribute, you also need'unsafe-hashes'in the directive. Without it, hashes only match full<script>and<style>blocks. - Hashing an external script against its SRI-style integrity digest is defined in CSP3 and works across current browsers. A CSP hash is not the same thing as a Subresource Integrity hash; see SRI vs CSP hash for the distinction.
Compute a hash
The hash is the base64 digest of the exact inline content, with no surrounding
tags and no trailing newline, written into the policy as 'sha256-<output>'. Paste
the snippet into the hash generator and it returns the
ready-to-use source. See
computing a CSP sha256 hash for worked examples.
Insecure values to avoid
The failure that defeats a nonce is reuse: a predictable, static, or cached nonce
lets injected script present the known value and run. For hashes, the trap is
'unsafe-hashes' applied broadly, since it widens matching to attributes; scope
it to the exact hashes you need. Never fall back to 'unsafe-inline' to "make the
nonce work"; it is ignored when a nonce is present and only confuses the policy.
What it protects against
Nonces and hashes are how a policy distinguishes the handful of inline scripts you
wrote from any inline script an attacker injects, which blocks the central XSS
vector that 'unsafe-inline' leaves open. Audit whether a policy actually relies
on them with the CSP evaluator.
Known bypasses and limitations
A leaked or guessable nonce is the practical bypass, so entropy and per-response uniqueness are not optional. Hashes are brittle against content changes: a build step that reformats or minifies inline code invalidates the stored hash, breaking the script until you recompute it.
Risks
The common operational risk is a deployment that caches a nonced page, freezing one nonce across many users, which both breaks legitimate scripts (when the cache and header diverge) and undermines the protection. The hash equivalent is shipping a code change without updating the hash. Roll changes out in Report-Only mode to catch both before they reach users.
Recommendation
Use a nonce when the page is rendered per request and you can inject a fresh value
into both the header and the markup; it handles dynamic inline code cleanly. Use a
hash when the inline content is static and you would rather not thread a nonce
through caching layers, or when you cannot set a request-time header (a hash works
in a <meta> policy). Either way, pair it with
'strict-dynamic'
so the trusted bootstrap script can load the rest. Set the remaining directives
explicitly so nothing falls back implicitly:
Content-Security-Policy:
default-src 'self';
script-src 'nonce-{RANDOM}' 'strict-dynamic';
style-src 'self';
img-src 'self';
font-src 'self';
connect-src 'self';
media-src 'self';
manifest-src 'self';
frame-src 'none';
worker-src 'self';
object-src 'none';
base-uri 'none';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
report-to csp-endpointReporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"This is the strict CSP the
OWASP CSP cheat sheet
and the web.dev strict CSP guide
recommend over host allowlists: only the scripts you marked run, and no
'unsafe-inline' weakens the policy.
Examples
A strict script policy using a nonce and 'strict-dynamic':
Content-Security-Policy:
script-src 'nonce-8IBTHwOdqNKAWeKl7plt8g==' 'strict-dynamic';
object-src 'none';
base-uri 'none'Allowing one static inline script by hash:
Content-Security-Policy: script-src 'self' 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc='Browser support
Nonce sources and sha256/sha384/sha512 hash sources are part of core CSP and
are widely supported across current browsers. The 'unsafe-hashes' extension for
attributes is also widely supported. External-script hash matching is now
widely supported across current browsers too.
FAQ
Should I use a nonce or a hash?
Use a nonce when the page is rendered per request and you can inject a fresh value into both the header and the markup; it handles dynamic inline code cleanly. Use a hash when the inline content is static or you cannot set a request-time header, since a hash works in a <meta> policy.
How do I generate a CSP hash?
Compute the base64 digest of the exact inline content, with no surrounding tags and no trailing newline, then write it into the policy as 'sha256-<output>'. A single whitespace change invalidates the hash. Paste the snippet into the hash generator and it returns the ready-to-use source.
See also
- Keywords
- Host source
- script-src directive
- style-src directive
- Setting up a CSP nonce
- Computing a CSP sha256 hash
- Hash generator tool