CSP — Refused to Load the Script Because It Violates the Policy
Debug Content Security Policy violations from console reports and report-only mode, read the directive that fired, and fix the policy or the resource delivery.
Symptoms
- Console shows Refused to load the script because it violates the following Content Security Policy directive: script-src 'self'
- Styles, images, or XHR calls fail with analogous connect-src, style-src, or img-src violations after a CSP rollout
- Inline event handlers and eval-based libraries stop working under the new policy
Possible causes
- The CSP whitelist omits the origin serving scripts, styles, or API calls
- Inline scripts or event handlers were left in place without unsafe-inline or nonces
- A third-party widget loads from an origin nobody added to the policy
- The header order or multiple CSP headers intersect, making the effective policy stricter than intended
Diagnosis steps
Read the exact violation in the console
DevTools → Console → filter by "Content Security Policy"Interpretation
Each violation names the directive and the blocked URL — the precise pairing of rule and resource that must be reconciled.
Inspect the effective headers
curl -sI https://app.example.com | grep -i content-security-policyInterpretation
Multiple CSP headers intersect (the strictest wins); one policy with many directives is easier to reason about than several competing headers.
Dry-run with report-only first
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reportInterpretation
Report-only mode logs violations without blocking, letting you collect the full violation set before enforcing anything.
Root cause
A Content Security Policy is enforced by the browser as a whitelist of sources per resource type. Any resource outside the directive whitelist — script, style, connection, image, font — is refused at load time. The policy and the actual delivery graph of the page disagree about which origins are legitimate.
Fix
Collect violations in report-only mode, add the origins you actually use per directive, replace inline scripts with nonces or hashes instead of unsafe-inline where possible, and deploy enforcement only after the report stream is clean.
Risk note
Adding unsafe-inline or a wildcard domain to silence one error permanently weakens the policy. Use nonces and specific origins.
Verify the fix
With enforcement on, the console shows zero violations, all functionality works, and the report endpoint receives no new reports during a full user journey.
Symptom walkthrough
The console violation is a complete incident report:
Refused to load the script from
'https://cdn.partner.example.com/widget.js'
because it violates the following Content Security Policy
directive: "script-src 'self'"
Rule: script-src ‘self’. Resource: a partner CDN. The disagreement is the entire bug. The sibling violations follow the same shape for other resource types — connect-src blocks the XHR, style-src blocks the inline style attribute, img-src blocks the tracking pixel. Read them as a list of (directive, origin) pairs your policy must explicitly allow or your delivery must relocate.
Fix in practice
Stage the rollout with report-only while you collect the real list:
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' 'nonce-r4nd0m' https://cdn.partner.example.com;
connect-src 'self' https://api.example.com;
style-src 'self' 'nonce-r4nd0m';
report-uri /csp-report;
Then enforce the same policy once reports go quiet:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m' https://cdn.partner.example.com 'strict-dynamic';
report-uri /csp-report;
Serve the nonce with each response and mark inline scripts with it — nonce-per-response keeps inline capability without the blanket unsafe-inline. And instrument the report endpoint: violations after deployment become a monitoring stream, not user-reported breakage.