CSP Nonce and Hash Generator Guide: Fresh Nonces and Content Hashes

A strict Content-Security-Policy that blocks inline scripts creates an immediate practical problem: many real pages contain inline scripts, from small configuration snippets to analytics bootstraps, and rewriting every one of them into external files is not always feasible. The naive solution is to add unsafe-inline to script-src, but that reopens the exact hole the policy was meant to close, because an injected inline script is then indistinguishable from a legitimate one. Nonces and hashes are the two mechanisms CSP offers to escape that dilemma. They let a page allow specific inline scripts, identified precisely, while continuing to block every other inline script, including injected ones.

The CSP Nonce and Hash Generator tool generates a random CSP nonce and the SHA-256 hash of pasted content, in your browser. Both operations happen locally, which matters for the hash case: you paste the exact inline content, and the tool computes its fingerprint without sending the content anywhere. A nonce is a random value generated fresh for a response, placed both in the CSP header and in the script tag it authorises, so the browser can match them. A hash is a fingerprint of the script's exact content, placed in the CSP header, so the browser can allow a script whose content matches the fingerprint and block any script whose content differs by even one character.

The difference between the two mechanisms decides which one to use. A nonce is tied to a single response: the server generates a fresh random value for each page load, puts it in the header and in the script tag, and the browser allows scripts carrying that nonce. Because the value changes on every load, an attacker who sees one page's nonce cannot reuse it on another page load, since the next load will carry a different value. A hash is tied to the content, not the load: the same script content always produces the same hash, so the hash can be written into a static header and never changed, as long as the script itself never changes. That makes hashes ideal for stable snippets such as a fixed analytics bootstrap, and nonces ideal for pages whose inline scripts change per user or per request, such as a script containing a user-specific configuration value.

Generating a good nonce has one absolute rule: it must be random and fresh per response. A nonce that is predictable, such as a timestamp or a counter, can be guessed by an attacker, and a nonce that is reused across responses can be copied from one page and replayed on another. The generator produces a random value, but randomness at generation time is only half the job; the server must generate a new value for each response and insert it into both the header and the tag. A nonce copied from a generator output and pasted into a static configuration file is no longer a nonce in any meaningful sense, because it is the same on every load, and an attacker who reads any page can reuse it anywhere. The generator is a way to understand the format and to test, not a substitute for per-response server generation in production.

A worked example you can follow

Take a sample inline script containing exactly 48 characters of code, for example a short configuration assignment. Paste it into the hash side of the tool, and the tool computes its SHA-256 hash. SHA-256 always produces a 256-bit fingerprint, and 256 divided by 8 is 32 bytes. Written as hexadecimal, each byte takes 2 characters, so 32 times 2 is 64 characters, and the hash in the header reads sha256- followed by a 64-character value. That length is a useful sanity check: a hash value that is not 64 hexadecimal characters long is not a SHA-256 hash, and something went wrong in copying. Place that value in script-src alongside self, and the browser will allow an inline script whose content hashes to exactly that value, and block any inline script whose content differs, even by a single space.

Now take the nonce side. Generate a random nonce from 16 random bytes. Encoded in base64, 16 bytes become 24 characters, because base64 encodes each group of 3 bytes as 4 characters, and 16 divided by 3 is 5 groups with 1 byte left over, giving 5 times 4, or 20 characters, plus 4 characters for the final partial group, which is 24 characters in total including padding. Place that value in the header as nonce- followed by the 24 characters, and place the same value in the script tag's nonce attribute. The browser compares the two, and allows the script only when they match exactly. If the page contains 3 inline scripts, all 3 tags must carry the same nonce for that response, and the header carries it once. If a fourth inline script appears without the nonce, it is blocked, which is the protection working as intended.

Take a third sample that shows the failure mode. Suppose a team generates one nonce, writes it into a static header, and serves it for a week. During that week the site serves 10,000 page loads, all carrying the same nonce. An attacker who views any one page learns the nonce, and can then craft an injected script carrying that nonce, which the browser will allow on all 10,000 loads, because the value matches. The reuse turns 1 exposed value into 10,000 vulnerable loads, which is 10,000 times the exposure of a per-response nonce, where a single exposed value endangers only the 1 load it was generated for. That arithmetic is why freshness is not a nicety but the entire security property of a nonce.

Mistakes to avoid

The first mistake is reusing a nonce across responses, which converts a per-load secret into a site-wide password that anyone can read. The second mistake is keeping unsafe-inline in script-src alongside a nonce or hash, which makes the precise mechanism pointless, because the browser will allow inline scripts through the unsafe-inline path regardless. The third mistake is hashing the wrong content: a hash covers the exact script text, including whitespace, so a reformatted script produces a different hash and gets blocked, while the team concludes, wrongly, that hashes do not work. The fourth mistake is pasting secrets into a hash generator along with the script. A hash is computed locally here, but the habit of pasting sensitive content into web tools is dangerous in general, so keep scripts free of secrets before generating anything from them.

The CSP Generator tool builds the header that will carry the nonce or hash, and the Hash Generator tool computes one-way fingerprints for text more generally. For background on placing CSP in a wider security review, see How to Configure HTTP Security Headers.

Checking the result before you publish it

Treat a generated nonce or hash as a test value, not a production credential. In production, nonces must come from the server's random generator on every response, and hashes must be recomputed whenever the script changes, then verified by loading the page and confirming the intended script runs while an unlisted inline script is blocked. Test both directions: the allowed script working, and an unlisted script failing, because a policy that allows everything has not been tested at all. Keep a note of which mechanism each inline script uses and why, so a future edit that changes a script's content also updates its hash, rather than breaking the page mysteriously. A nonce that is fresh per response, or a hash that matches its script exactly, is a precise allowlist doing real work.