Fix guide · high · csp_inline_script_blocked

Inline <script> blocked by declared CSP

What this rule means

An inline <script> on the served page would be blocked by your declared CSP. The browser refuses to execute it — your page is shipping broken JavaScript that only fails on browsers enforcing the policy.

Why it matters

When a CSP is hash-pinned (script-src 'sha256-abc...' 'sha256-def...'), the browser computes the SHA-256 of each inline <script> element and checks whether the digest appears in the policy. If not, the script is blocked at runtime — no execution, console error, page-functionality regression.

Vibecheck simulates this check: it parses the served HTML, extracts every inline <script> block, hashes the contents, and compares against the hashes in your declared CSP. When the simulation finds a script your policy doesn't allow, this rule fires.

The fix is almost always one of:

  1. The script content changed but the policy wasn't updated. Build pipelines that auto-generate CSP hashes need to re-run on every content change. If your CI only runs the hash regenerator on schema-defined templates (not on JSON-LD blocks that change per-page), you'll see drift.
  2. The script was added by a contributor who didn't know about the hash pin. Someone pasted in an analytics snippet, a third-party widget, or an inline event handler that bypassed the CSP-aware build step.
  3. The policy was hand-crafted from an outdated snapshot. Hash-pinned CSPs are notoriously brittle; if you wrote yours by hand once and forgot it's there, content drift kills it silently.

The user-visible symptom is "the page works in dev but is missing functionality in production" — because dev usually has no CSP enforcement, but the production CSP header (or meta tag) does.

This is different from csp_unsafe_inline_scripts (the standalone policy is permissive). Here the policy is STRICT, and there's a real script in the page that violates it. The high severity reflects that the bug breaks functionality, not just security.

How to fix it

Three fix paths depending on root cause:

1. Regenerate the policy from current content. If you control the build, run your CSP-hash generator on every deploy. Vibecheck ships scripts/build-csp.ts (in the source repo) as a reference implementation — pass it your HTML and it emits the hash list.

# Regenerate CSP for all production HTML
tsx scripts/build-csp.ts public/*.html > public/_headers

2. Move the inline content out. The most robust answer is "don't ship inline scripts at all." External <script src="..."> with SRI is easier to govern. Even JSON-LD blocks can move to a <script src="/structured-data.json"> if you accept the extra request.

3. Move to nonce-based CSP.

Content-Security-Policy: script-src 'nonce-r4nd0m...' 'self'

The server generates a fresh <script nonce="r4nd0m..."> value on every request. Hash mismatches become impossible because there are no hashes — but you need a server that can rewrite HTML on every request, which is hard on a fully-static CDN deploy. Cloudflare Pages Functions can do this; pure static hosting cannot.

Why we don't recommend just adding the new hash: The dev who'll add the hash without thinking about it might be adding a hash for an attacker's injected script if the inline content was reflected user input. The correct response is "verify the script content is intentional," then add the hash if so.

Did vibecheck flag this on your app?

If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.

Run another inspection