CentralCSP
PoliciesContent-Security-PolicyValues

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>
ValueStatusDescription
Nonce source 'nonce-<base64>'✅ GoodA fresh, unguessable per-response token matching the element's nonce attribute.
Hash source for inline content✅ GoodA sha256, sha384, or sha512 digest of the exact inline script or style text.
Hash source for external scripts✅ GoodMatches the script's SRI-style integrity digest. Widely supported across current browsers.
Hash for event handlers, with 'unsafe-hashes'❌ RiskyWidens 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 nonce attribute. 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, or sha512. The algorithm in the source must match the one you computed.
  • To match an inline event handler (such as onclick=) or a style= 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-endpoint
Reporting-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

Sources

On this page