Refused to execute inline script
The most common CSP error there is — and the one with the most tempting wrong answer.
The error
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self'". Either the
'unsafe-inline' keyword, a hash ('sha256-…'), or a nonce ('nonce-…')
is required to enable inline execution.
Firefox words it differently — "The page's settings blocked an inline script (script-src-elem) from being executed" — and Safari differently again. Same cause.
What happened
Your page contains a <script> block written directly into the HTML, and your policy
does not allow inline script. The browser refused to run it. Nothing was fetched and nothing was
executed.
Why CSP does this
An injected payload is an inline script. If the browser had a way to tell your inline script from an attacker's, this would not need to be a rule — but it does not, so CSP blocks all inline script unless the script can prove it came from you. Proving it is what nonces and hashes are for.
Do not do this
script-src 'self' 'unsafe-inline'
It makes the error go away and removes almost all of the protection you deployed CSP for. An attacker who finds an injection point on any page now gets script execution. If you only change one thing after reading this page, let it be not doing that.
Three fixes that work
1. A nonce — best for anything server-rendered
Generate a fresh random value on every response, put it on the script tag and in the header. They must match, and the value must never be reused across responses.
Content-Security-Policy: script-src 'nonce-r4nd0mV4lu3' 'strict-dynamic'
<script nonce="r4nd0mV4lu3"> // your inline code </script>
At least 128 bits of cryptographically secure randomness. In PHP that is
base64_encode(random_bytes(16)). See
what goes wrong with nonces before you ship it.
2. Move the code to a file
If the script does not need anything from the server, put it in a .js file served from an
allowed origin and the problem disappears. This is usually the right answer for anything static.
3. A hash — for inline script that genuinely never changes
Take the SHA-256 of the exact bytes between the tags and add it to the directive. No server-side generation needed, which makes it the only option on a static host — but it breaks the moment the script changes by one character.
script-src 'self' 'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8='
See it happen: XSS via user input
If you want to see exactly what you are switching off when you reach for 'unsafe-inline': this page takes user input and reflects it. Run it unprotected, then run it with the policy on.
Run it with no policy ↗Run the same page protected ↗
Identical page both times — the only difference is the Content-Security-Policy header. On the Report URI demo site.
Test it safely
Do not enforce your fix straight away. Ship it as
Content-Security-Policy-Report-Only alongside the policy you already have. The page keeps
working either way, and the violation reports tell you whether you caught every inline script — including
on the pages you did not think to check.