Learn · Strict CSP

Nonces, hashes, and allowlists

Three ways to allow inline and dynamically loaded scripts under a Content Security Policy — and how to pick the right one for your application.

Why this choice matters

The single biggest CSP decision is how you handle inline scripts. The naive answer — 'unsafe-inline'— defeats the entire policy: it's the directive equivalent of leaving the front door open while bolting every window shut.

Modern guidance from Google's web.dev team and the W3C is to use either a per-response nonce or a per-block hash, combined with 'strict-dynamic' so trusted scripts can transitively load other scripts. The question is which mechanism fits your delivery model.

The three approaches

Most production policies end up combining elements. A typical strict CSP uses a nonce for first-party inline scripts plus hostname allowlists for a small number of well-vetted vendors.

  • Allowlist (host-based)

    When to reach for it: Easy to deploy on legacy applications. Lets you add CSP without changing how the application emits markup.

    How it looks: Each directive lists explicit hostnames or scheme sources, e.g. script-src 'self' https://cdn.example.com https://www.googletagmanager.com.

    Tradeoffs: Brittle and frequently bypassable — if any allowed origin hosts a JSONP endpoint or a vulnerable script, attackers can route around the policy. Modern guidance treats pure allowlists as a starting point, not the destination.

  • Nonce-based

    When to reach for it: Right for HTML pages that are rendered server-side or at the edge — anywhere you can inject a fresh random value per response.

    How it looks: Generate a cryptographically random value per request, set it on the response header (script-src 'nonce-{value}') and on every inline <script nonce="{value}">. Pair with 'strict-dynamic' so trusted scripts can load further trusted scripts.

    Tradeoffs: Requires per-response control over both header and HTML. Cached HTML breaks unless you cache the nonce alongside the body, or vary on a fresh value.

  • Hash-based

    When to reach for it: Right for static or fully cacheable HTML — single-page apps, marketing pages, generated sites — where the inline content doesn't change per request.

    How it looks: Compute a sha256/sha384/sha512 hash of each inline script or style block and add 'sha256-{hash}' to the corresponding directive.

    Tradeoffs: Tedious to maintain by hand whenever inline content changes. A build-step or scanning tool is essentially required to keep hashes in sync with deployed markup.

A nonce, end to end

The nonce is a random value generated on the server per response. It appears in the header and on every inline tag you want to allow:

# Response header (regenerated per request)
Content-Security-Policy:
  script-src 'nonce-r4nd0mB4se64Value' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

# Matching inline script in the HTML body
<script nonce="r4nd0mB4se64Value">
  // Inline application bootstrap goes here.
</script>

Reference: content-security-policy.com — nonces

A hash, for static markup

When the inline content is fixed at build time, hash the script body (byte-for-byte) and add the encoded value to the directive:

Content-Security-Policy:
  script-src 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=';
  object-src 'none';
  base-uri 'none';

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

Need to compute one? Use the hash calculator — it builds CSP-ready sha256, sha384, and sha512 values for inline scripts and styles.

What 'strict-dynamic' actually does

When a trusted script (one that matches a nonce or hash) inserts a new <script> into the DOM, the new script inherits trust. This lets bundlers, feature-flag SDKs, and tag managers continue to work without maintaining sprawling host allowlists for every CDN their dependency tree might pull from.

Reference: web.dev — Mitigate XSS with a strict CSP

Get started

Let Consepo wire up the strict policy

Run a browser-rendered scan and Consepo classifies inline blocks, suggests nonce or hash usage, and exports a deployable strict CSP for your stack.