Nonces
How CSP script-src nonces work, how to generate them safely, and the mistakes that silently disable them.
A nonce allowlists a single inline script by tagging it with a random value that also appears in the header. Use nonces when your server renders pages dynamically and can emit a fresh value on every response.
How it works
Put the same random value in the script-src directive and on the script tag:
Content-Security-Policy: script-src 'nonce-2726c7f26c'
<script nonce="2726c7f26c">
console.log('this inline script runs');
</script>
The browser executes the inline script only when the tag's nonce attribute
matches the value in the header. An attacker who injects a <script> cannot
guess the current nonce, so the injected script is blocked.
Rules that make a nonce safe
A nonce only provides protection if all of these hold:
- Fresh on every response. Generate a new value per HTTP response. A nonce that is reused across responses, or hard-coded in your template, gives an attacker a value they can copy.
- Unpredictable. Use a cryptographically secure random source with at least 128 bits of entropy, then base64-encode it. Do not derive it from the time, a counter, or the session ID.
- Server-generated. The value must come from the server that sets the header. You cannot add a nonce from client-side JavaScript, because the header is already parsed by then.
Generate it from a cryptographically secure random source on the server, for
example 16 bytes from crypto.randomBytes base64-encoded, on every response.
unsafe-inline is ignored when a nonce is present
Browsers that support nonces ignore 'unsafe-inline' whenever a nonce or hash
is present in the same directive. This lets you keep 'unsafe-inline' as a
fallback for very old browsers without weakening modern ones:
script-src 'nonce-2726c7f26c' 'unsafe-inline'
Modern browsers honor the nonce and ignore 'unsafe-inline'. Ancient browsers
that do not understand nonces fall back to 'unsafe-inline'.
Nonces and dynamically loaded scripts
A nonce applies to the exact tag it is placed on. If that script then injects
more <script> elements, those children do not inherit the nonce and will be
blocked. To let a trusted script load its own dependencies, combine the nonce
with strict-dynamic.
Common mistakes
- Rendering the nonce into a cached HTML fragment, so every visitor gets the same value.
- Forgetting the
nonceattribute on the tag while it is in the header, or the reverse. - Adding a nonce to an external script via
src. That works, but the nonce is only needed when the host is not already allowlisted.