Refused to execute inline script because it violates the following Content Security Policy directive
A fix for CSP blocking inline scripts: what 'refused to execute inline script' means, how to authorize scripts with nonces or hashes instead of weakening the policy, and when moving code to external files is the right call. Use when a deploy breaks JavaScript with CSP errors or when tightening a policy. Triggers: 'violates the following Content Security Policy', 'refused to execute inline script'. Not for: CORS errors, mixed-content blocks, or ad-blocker interference.
Refused to execute inline script because it violates the following Content Security Policy directive
TL;DR
Your Content Security Policy blocks inline scripts by default, and the browser just refused to run one. Do not reach for 'unsafe-inline'. Instead, authorize the script properly: have the server generate a random nonce per response and put it in both the CSP header and the script tag, or hash the exact script body and allowlist the hash. If the inline script is yours and static, moving it to an external file is often simplest.
Refused to execute inline script because it violates the following Content Security Policy directiveUse this when
- The console shows this error after a deploy or a policy change
- You are adding analytics, chat widgets, or A/B testing snippets to a CSP-protected page
- A security review asks you to tighten script-src without breaking the app
- Inline event handlers like onclick stopped working
Not for this skill when
- The console shows a CORS error instead (different mechanism)
- The script fails to load from its src URL (that is a 404 or network issue)
- An ad blocker or extension is interfering (test in a clean profile first)
Steps
- Read the violated directive in the full console message. It names the directive, usually script-src, and quotes the policy that blocked the script. That tells you exactly which allowlist to extend.
Expected: you know the directive and the current script-src value.
- Prefer a nonce. On each response, generate a random value server-side, add
'nonce-[random value]'to the script-src directive in the CSP header, and add the matchingnonceattribute to every script tag you author. Because the value changes per response, injected markup cannot guess it.
Expected: your inline scripts run, and the console is clean of CSP errors.
- Or use a hash for truly static scripts. Compute the SHA-256 of the exact script body and add
'sha256-[base64 hash]'to script-src:
echo -n "[script body]" | openssl dgst -sha256 -binary | openssl base64The browser prints the expected hash in the console error, so you can compare directly. Expected: the hash in your policy matches the one the browser expects, and the script runs.
- For scripts you control, consider moving them out of the page. An external file from your own origin is allowed by
'self'with no nonce or hash bookkeeping, and it is cacheable.
Expected: the inline script is gone, the policy stays tight, and nothing else changed.
Variant: style-src blocking inline styles
Same idea, different directive. Nonces and hashes work for style-src too. Inline style attributes are the usual casualty when teams tighten styles.
Variant: CSP blocking eval or new Function
That is script-src without 'unsafe-eval'. Some libraries need it. Prefer a build that avoids eval (most ship one); granting 'unsafe-eval' weakens XSS protection almost as much as 'unsafe-inline'.
Variant: third-party widget injecting inline scripts
Widgets that document.write inline scripts cannot carry your nonce. Either the vendor offers a CSP-compatible snippet, or you isolate the widget in an iframe or a separate page with its own looser policy.
Variant: report-only mode first
Deploy new policies as Content-Security-Policy-Report-Only with a report endpoint before enforcing. You will see what would break without breaking it.
Why this happens
CSP treats every inline script as untrusted by default because an attacker who can inject markup into your page can also inject an inline script, but cannot know your per-response nonce or pre-registered hash. Blocking inline scripts removes the easiest XSS payload shape entirely, which is why the default is deny.
Edge cases and pitfalls
- A nonce must be random per response. A static nonce in a template is just 'unsafe-inline' with extra steps.
- Hashes break on any byte change, including a templating engine reformatting whitespace. If the script is dynamic, use a nonce instead.
- Single-page app builds change script contents every deploy. Automate hash generation in the build or use nonces.
- 'unsafe-inline' as a "temporary" fix has a way of becoming permanent. If you must use it, file the follow-up task to remove it before you merge.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_E-JZItazgsn2L035jGW9uQ
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.