# How to generate a CSP hash (sha256) for an inline script (/en/blog/csp-hash-sha256)





A CSP hash allowlists one exact inline script by the fingerprint of its content. A Content Security Policy (CSP) is an HTTP response header that tells the browser which scripts it may run, and by default it blocks inline scripts. A hash source lets you keep one specific inline block working without re-enabling all inline script.

The short version: compute a SHA-256 digest of the exact text inside the `<script>` tag, base64-encode it, and add it to `script-src` as `'sha256-...'`. The browser hashes each inline script it finds and runs only the ones whose hash you listed. If you would rather skip the math, paste the snippet into our [hash generator](/tools/csp-hash) and copy the source value it returns.

New to inline handling? [Why you should never use unsafe-inline in CSP](/en/blog/unsafe-inline-csp) explains why hashing beats `'unsafe-inline'`.

## What a CSP hash is [#what-a-csp-hash-is]

A hash source is a base64-encoded cryptographic digest of an inline script's content, written into your policy. When the browser meets an inline `<script>`, it computes the same digest over that script's content and checks it against the hashes in the directive. A match runs; anything else is blocked.

Because the hash is tied to the exact bytes of the script, an attacker who injects different inline code cannot match it, so the injected script never runs. That is what makes a hash a safe way to allow specific inline blocks.

## The three source forms [#the-three-source-forms]

CSP supports three digest algorithms, written with the algorithm as a prefix:

* `'sha256-...'`
* `'sha384-...'`
* `'sha512-...'`

All three are widely supported in current browsers. SHA-256 is the common choice; the longer variants produce longer values for no security benefit at typical script sizes. The value after the prefix is the **base64** encoding of the raw digest, not the hex form.

## The hash is over the exact content (whitespace matters) [#the-hash-is-over-the-exact-content-whitespace-matters]

The digest is computed over the **exact text between the opening and closing tags**, not the tags themselves and not any attributes. Every character counts: spaces, tabs, trailing newlines, and capitalization are all part of the content.

```html
<script>console.log("hello");</script>
```

The hash for that block covers exactly `console.log("hello");`. Add a leading space, a trailing newline, or change a quote style and the digest changes, so the source you listed no longer matches and the browser blocks the script. Compute the hash from the final, minified bytes you actually serve, not from a prettified copy.

## Compute the hash [#compute-the-hash]

For a single snippet, paste the inline code into the [hash generator](/tools/csp-hash) and it returns the `'sha256-...'` source ready to drop into the policy, with no command line and no whitespace mistakes.

In a build step, compute it in [Node.js](https://nodejs.org/en/docs) with the built-in `crypto` module:

```javascript
const crypto = require("crypto");

function cspHash(content) {
  const digest = crypto.createHash("sha256").update(content, "utf8").digest("base64");
  return `'sha256-${digest}'`;
}

console.log(cspHash('console.log("hello");'));
```

This produces the same value, computed from the exact UTF-8 bytes of the script content, so wire it into your build to keep hashes in sync as the inline code changes.

## Where the hash goes [#where-the-hash-goes]

Add the source to the directive that governs the resource. For inline scripts that is `script-src`; for inline styles it is `style-src`. You can list several hashes to allow several inline blocks.

```http
Content-Security-Policy: script-src 'self' 'sha256-q1V8...=' 'sha256-Hk9p...='
```

You can mix hashes with `'self'` and host sources for your external files, and the hashes only ever apply to inline content. If the directive also has a nonce or a hash, the browser ignores `'unsafe-inline'` in that same directive, so a leftover `'unsafe-inline'` does not undo your hashes (remove it anyway).

## Hash vs nonce, when to pick which [#hash-vs-nonce-when-to-pick-which]

Hashes and [nonces](/en/blog/csp-nonce-setup) solve the same problem, allowing specific inline scripts, but suit different content.

* **Use a hash for static inline code** that does not change between responses: a build-time inline bundle, a fixed analytics snippet, a small constant block. You compute the hash once at build time and it stays valid until the content changes.
* **Use a nonce for dynamic, server-rendered pages** where the inline content or the set of inline scripts varies per request. A nonce is a fresh random value per response and does not depend on the script's content.

A hash needs no per-request work, which makes it ideal for static hosting and cached pages. A nonce needs the server to generate and inject a value on every response. Many sites use hashes for fixed snippets and a nonce for server-rendered blocks.

## The inline-handler caveat ('unsafe-hashes') [#the-inline-handler-caveat-unsafe-hashes]

A plain hash source allowlists inline `<script>` **blocks**. It does **not** cover inline event handler attributes like `onclick="..."` or `javascript:` URLs. Allowing a handler by its hash requires the separate `'unsafe-hashes'` keyword, which loosens the policy and is best avoided.

```html
<!-- Hashing this needs 'unsafe-hashes', which weakens the policy -->
<button onclick="saveForm()">Save</button>
```

The better fix is to remove the inline handler and bind it from a hashed or nonced script with `addEventListener`:

```javascript
// markup: <button id="save">Save</button>
document.getElementById("save").addEventListener("click", saveForm);
```

That keeps the policy strict and avoids `'unsafe-hashes'` entirely.

## See it in your reports [#see-it-in-your-reports]

When a hash does not match (often a whitespace change you missed), the browser blocks the script and reports a violation. CentralCSP collects those reports from real traffic and groups them, so a hash that drifted out of sync shows up as a recurring block instead of a silent breakage. You can [start a free trial](/register) and point a Report-Only header at it while you roll out hashes.

For the full directive and keyword reference, see the [CSP policy reference](/en/docs/web-security/policies/content-security-policy).

<img alt="Violation rows whose blocked origin reads inline rather than a host" src="__img0" width="1359" height="248" />

## Frequently asked questions [#frequently-asked-questions]

### Why does my CSP hash not match the script? [#why-does-my-csp-hash-not-match-the-script]

Almost always whitespace. The hash covers the exact text between the tags, including leading or trailing spaces and newlines. Compute it from the final served bytes, not a formatted copy, or use the [hash generator](/tools/csp-hash) on the exact snippet.

### Should I use a hash or a nonce? [#should-i-use-a-hash-or-a-nonce]

Use a hash for static inline scripts that rarely change, and a nonce for dynamic, server-rendered pages. Hashes need no per-request work; nonces do not depend on the script's content.

### Can a CSP hash allow an inline onclick handler? [#can-a-csp-hash-allow-an-inline-onclick-handler]

Not with a plain hash. Allowing an inline event handler by hash needs the `'unsafe-hashes'` keyword, which loosens the policy. Remove the inline handler and bind it with `addEventListener` instead.

## Sources [#sources]

* [MDN, script-src directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src)
* [MDN, CSP guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)
* [W3C CSP Level 3 specification](https://www.w3.org/TR/CSP3/)

## Related [#related]

* [Why you should never use unsafe-inline in CSP](/en/blog/unsafe-inline-csp)
* [strict-dynamic explained](/en/blog/strict-dynamic-csp)
* [How to set up a CSP nonce, per request](/en/blog/csp-nonce-setup)
