When a page breaks and the console says “Refused to load … because it violates the following Content Security Policy directive”, the fix is almost never to disable CSP. Read the message — it names the directive and the blocked URL — widen that one directive, and keep the header. Disabling it removes your strongest defence against injected scripts to save a five-minute fix.
How do I read a “blocked by CSP” error?
Every CSP console message has the same three parts. Take this real-world shape:
Refused to load the script 'https://cdn.vendor.com/widget.js' because it violates the
following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem'
was not explicitly set, so 'script-src' is used as a fallback.- What was blocked —
https://cdn.vendor.com/widget.js. If it saysinline, it's a script or style written into the HTML; ifeval, some code calledeval()ornew Function(). - Which directive did it —
script-src 'self'. This is the exact rule to widen. - The fallback note — browsers tell you when a more specific directive was missing and a general one applied. It's a hint that you may want to set the specific one instead of loosening the general one (see script-src, default-src and the fallback chain).
Open DevTools → Console, filter for “Content Security Policy”, and you have the complete list of what to change. Firefox and Safari word it slightly differently but include the same three parts (MDN: CSP violation reporting).
How do I fix it without disabling CSP?
Match the blocked thing to the smallest change that allows it:
- A third-party script host → add exactly that origin to
script-src:script-src 'self' https://cdn.vendor.com. Nothttps:, not*. - An inline script → give it a nonce or hash instead of adding
'unsafe-inline'— here's how. - An image, font or stylesheet host → add the origin to
img-src,font-srcorstyle-src. Fonts from Google need bothfonts.googleapis.com(style) andfonts.gstatic.com(font). - An API or websocket call →
connect-srcwith the API origin (wss://for sockets). - An embedded iframe (YouTube, Stripe, maps) →
frame-srcwith that origin. Don't confuse it with frame-ancestors, which controls who may frame you. - Base64 images (
data:) →img-src 'self' data:is normal.data:inscript-srcis not — that's a script injection vector. evalblocked → find the library that needs it (often an old templating or charting lib) and upgrade it; add'unsafe-eval'only as a documented temporary exception.
<meta http-equiv> tag in the template, a second header from a CDN rule, or a duplicated directive (only the first copy counts). Multiple policies are combined, so the strictest wins. Paste the exact header the browser received into the CSP Checker — its directive table shows duplicates struck through and lists every source the policy came from.Is there ever a good reason to disable CSP?
For debugging on your own machine, yes: a browser extension that strips the header lets you confirm the breakage is CSP-related in seconds. That is the only legitimate use. Removing the header in production to make a widget work trades a real security control for a convenience, and it rarely stays temporary. If a vendor tells you their product “requires disabling CSP”, what they mean is that they haven't documented which origins it loads from — ask for the list, or read it off the console errors.
If you genuinely can't maintain a policy yet, the honest middle ground is Report-Only mode: it blocks nothing, breaks nothing, and keeps collecting the list of what a real policy would need.
For the site owner and for the developer
For the site owner (plain English)
If a page stopped working after a security change and someone proposes “turning off the CSP”, push back once: the browser's error message says exactly which external resource was blocked, and allowing that single origin is a one-line change. Turning the policy off means any future injection bug on the site runs unopposed. A reasonable compromise while the fix is scheduled is to switch the header to Report-Only for a few days rather than delete it.
Enter your domain: the checker shows the policy your visitors actually receive, including duplicates and meta-tag copies that often explain 'but I already changed it'.
See the policy the browser gets →For the developer (exact syntax)
Before — the policy that broke a Stripe checkout and a Google Font:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'After — each blocked origin added to the directive the console named, nothing else loosened:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://js.stripe.com;
frame-src https://js.stripe.com https://hooks.stripe.com;
connect-src 'self' https://api.stripe.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
object-src 'none'; base-uri 'self'# Reproduce a CSP block from the command line — read the header the server actually sends
curl -sI https://example.com/ | grep -i content-security-policyIf you manage the header at the CDN (Cloudflare Transform Rules, Netlify _headers, Vercel vercel.json), remember that a header set there and one set in the app are both delivered — pick one place and delete the other.