CSP Hash Generator
Paste a whole HTML page, or drop the file, and get the Content Security Policy hash for every inline script and style block in it — computed over exactly the text the browser hashes, so it matches. Event handlers, style attributes, external scripts and JSON-LD are all accounted for, and the result is ready to paste into script-src and style-src.
or drop an .html file on the box. Files are read on this device and never uploaded.
Add to your policy
Merge these into your existing script-src and style-src. 'self' is included as a starting point; keep any other sources you already allow.
What was found
What a CSP Hash Is
A Content Security Policy without 'unsafe-inline' blocks every inline script and style on a page, which is most of its protection against cross-site scripting. A hash source lets one specific block through: the policy lists the base64-encoded SHA-256, SHA-384 or SHA-512 digest of the block's contents, and the browser runs the block only if the digest of what it received matches.
Because the hash covers the exact content, any change — a single space — produces a different hash and the block is refused. That is the point: a script injected by an attacker cannot match a hash they did not know in advance.
What Exactly Gets Hashed
The browser hashes the text between the opening and closing tags, not the tags themselves. Every character counts, including the line break straight after <script> and the indentation before </script>. The text is encoded as UTF-8, and it is the text after the page has been parsed, not the bytes in your file.
That last point is the one that catches people. Before the HTML parser reads anything, it converts every Windows line ending (CRLF) and every lone CR into a plain LF. A hash computed with openssl or a build script over a file saved with CRLF endings is a hash of bytes the browser never sees, and it fails without any other symptom. We tested it: the same page served with a hash of the CRLF text was blocked, and with a hash of the LF text it ran. Git on Windows makes this easy to hit — the repository stores LF, the working copy has CRLF, and a hash computed on a Windows machine differs from one computed in CI on Linux.
This tool follows the browser's steps. Whole pages go through the browser's own HTML parser, which does not run the scripts, and files are converted to LF before anything is hashed. When a file contained CRLF endings, it tells you.
Why a Hash Stops Matching
If the browser still refuses a script you have a hash for, the content it received differs from what you hashed. The usual reasons:
- Something rewrote the page between your build and the browser: a CDN optimisation such as HTML minification, a server-side template, or a plugin that injects code.
- A formatter or minifier changed the whitespace inside the tags after the hash was computed.
- The hash was computed over a file with Windows line endings.
- The page is not served as UTF-8 and the script contains non-ASCII characters.
- The hash included the <script> tags, or a trailing newline added by echo on the command line.
The fastest diagnosis comes from the browser. Chrome and Edge print the hash they computed in the console error for a blocked inline script. Paste that error into the optional box above and the tool tells you whether any block you pasted produces it. If none does, the browser is receiving different content from the page you have, and the list above says where to look.
Event Handlers, Style Attributes and 'unsafe-hashes'
A hash in script-src covers script elements only. Inline event handlers such as onclick="…" stay blocked even when their hash is listed, unless the directive also contains 'unsafe-hashes', which extends hash matching to attributes. The same applies to style="…" attributes and style-src.
The keyword is named unsafe for a reason: it allows any attribute with the matching content, anywhere on the page, so an attacker who can inject markup can reuse an allowed handler. Treat it as a step on the way to a strict policy. The lasting fix is to attach handlers with addEventListener in a script file and to move inline styles into a stylesheet.
What Does Not Need a Hash
Scripts loaded with a src attribute are allowed by host sources such as 'self' or their URL, not by a hash of inline content. Data blocks — a script element with a type the browser does not execute, such as application/ld+json for structured data — are never run, so CSP ignores them and they need no hash. Import maps and speculation rules look like data but are treated as inline scripts, and do need one; we checked, and Chrome blocks an import map that has none.
Hash or Nonce?
A nonce is a random value generated for each response and placed both in the policy header and on each allowed script's nonce attribute. It suits pages rendered by a server, where every response can carry a fresh value, and it copes with scripts whose content changes between requests. A hash suits static sites and cached HTML, where the content is fixed and there is no way to send a new value with every response.
One detail catches people during a migration: once a policy contains any hash or nonce, browsers ignore 'unsafe-inline' in the same directive. Adding the first hash can therefore break every other inline script you had forgotten about. Scan the whole page, as this tool does, before deploying.
Building a Browser Extension?
Many "Refused to execute inline script" errors come from Chrome extensions rather than websites. A Manifest V3 extension's own pages run under a policy that cannot be relaxed beyond script-src 'self', so neither 'unsafe-inline' nor a hash will get an inline script running there. Move the code into a .js file in the extension and load it with a script src tag.
Related Tools
CSP hashes are SHA digests encoded as base64. For hex digests of any text — checksums, cache keys, integrity checks — use the Hash Generator.
When a hash stops matching after deployment, comparing the script in your source with the script the browser received shows exactly what changed. Compare them with the Diff Checker.
The part after sha256- is standard Base64, not hex, which is why a hex digest from a command-line tool will not work as-is. Convert between them with the Base64 Encoder.
JSON-LD blocks need no hash, but they still have to be valid JSON for search engines to read them. Validate them with the JSON Formatter.
References
- W3C: Content Security Policy Level 3
- MDN: Content-Security-Policy script-src
- WHATWG HTML: Preprocessing the input stream
- Chrome for Developers: Manifest V3 content security policy
Frequently Asked Questions
How do I generate a CSP hash for an inline script?
Paste the whole page, or just the code between the script tags, into the generator above. It computes the SHA-256 digest of exactly the text the browser hashes and gives you the source expression, such as 'sha256-…', along with a script-src line to paste into your policy. SHA-384 and SHA-512 are available too.
Why doesn't my CSP hash match?
Because the browser hashed different content from you. Common causes are Windows line endings in the file you hashed — browsers convert them to LF before hashing — whitespace changed by a formatter or minifier, a CDN or template rewriting the script, or the script tags being included in the hash. Paste the console error into the tool to see whether any block on your page produces the hash the browser wanted.
Do I need to hash JSON-LD structured data?
No. A script element with type application/ld+json is a data block: the browser never executes it, so Content Security Policy does not apply and no hash is needed. Import maps are the exception among non-JavaScript script types; they are treated as inline scripts and do need a hash.
How do I allow an onclick handler with CSP?
A hash on its own does not work for event handler attributes. You need the handler's hash and the 'unsafe-hashes' keyword in script-src, which allows any attribute with that exact content. The safer fix is to remove the attribute and attach the handler with addEventListener in a script file, which needs no hash at all.
Should I use a CSP hash or a nonce?
Use a nonce when a server renders each response and can generate a fresh random value for it; use a hash when the HTML is static or cached and the inline content does not change. Note that once a directive contains a hash or a nonce, browsers ignore 'unsafe-inline' in it, so every inline script needs one or the other.
Does a CSP hash include the script tags?
No. The hash covers only the text between the opening and closing tags, but all of it, including the line break after the opening tag and any indentation before the closing tag. Changing that whitespace changes the hash.