CSP Blocks Required Scripts: Diagnose the Directive and Fix Trust Narrowly
A blocked script may need a nonce, a different directive or a corrected upstream header. Trace the actual restriction before expanding the policy.
Read the effective directive before adding a domain
A CSP error should send you to the policy that actually blocked the operation. Record the page, blocked resource or inline code, effective directive and whether the violation was enforced. A script-loading problem, an inline handler and an analytics network request may need different changes.
In a local Chromium fixture, a document allowed scripts from its own origin but did not authorize an inline script. The script did not initialize the application. Returning a fresh nonce in the response policy and on that intended script restored the action without enabling all inline code. The next response used a different nonce.
That is a specific repair for a specific failure. It is not a reason to add a nonce to arbitrary user-controlled markup or to permit every third-party host mentioned by the browser.
Inspect every policy that reaches the browser
Start in the document's Network response headers and check for Content-Security-Policy. Review any policy delivered in a meta element too. The application, reverse proxy and CDN can each contribute a policy.
Multiple enforced policies do not combine into one larger allowlist. A permissive application header cannot cancel a stricter policy added upstream. The CSP specification (opens in a new tab) defines how policies are evaluated, including enforced and report-only policies.
Do not use a header-only command as the only check if a template or middleware behaves differently on GET. Reload with the browser's actual session and consent state, then verify the headers on the resulting document.
Match the repair to the operation
| Browser observation | First configuration to inspect | Safer repair direction |
|---|---|---|
| Inline script is blocked | script-src or script-src-elem | Authorize the intended script with a nonce or hash |
| onclick is blocked | script-src-attr | Move the handler into approved script code |
| External script cannot load | Effective script-loading directive | Verify the dependency and authorize it narrowly |
| Script loads but its API request fails CSP | connect-src | Review the actual destination separately |
| Font or stylesheet is blocked | font-src or style-src family | Adjust the relevant directive, not script-src |
This is a diagnostic map. The browser may use a fallback directive when a more specific one is absent; read the reported effective policy rather than assuming a directive exists.
Give intended inline scripts a response-specific nonce
For a supported PHP application generating dynamic HTML, a minimal example is:
<?php
$nonce = base64_encode(random_bytes(24));
header("Content-Security-Policy: default-src 'self'; "
. "script-src 'self' 'nonce-{$nonce}'; "
. "script-src-attr 'none'; object-src 'none'; base-uri 'none'");
?>
<button id="save" type="button">Save</button>
<script nonce="<?= htmlspecialchars($nonce, ENT_QUOTES, 'UTF-8') ?>">
document.getElementById('save').addEventListener('click', () => {
document.getElementById('save').textContent = 'Saved';
});
</script>
Generate headers before output. The nonce in the policy and intended script must match. Generate a new unpredictable value for each response; do not paste the example's generated value into configuration. In a template engine, pass the server-generated value through its normal escaping mechanism.
The example deliberately keeps script-src-attr set to none and uses addEventListener instead of an inline event-handler attribute. It is a teaching policy, not a complete configuration for an application that embeds videos, uses external fonts or submits forms to other origins.
Nonce-bearing HTML also needs coherent caching. Do not let one layer cache a document body while another independently generates a different policy nonce. Test the response through the actual CDN path, not just the local template.
Use hashes for stable code and audit dynamic loading
A hash can authorize a fixed inline script without a per-response value. Its bytes, including whitespace, must match the hash. Recompute it when the script changes instead of widening the policy after a build modifies the code.
Some trusted scripts load further scripts. A strict policy using strict-dynamic can propagate trust from a nonce- or hash-authorized script, but that delegation deserves review. Do not add it merely because a tag manager failed: it changes which code can authorize later loads. MDN's script-src reference (opens in a new tab) documents these mechanics.
For analytics, separate consent from permission. A script being allowed by CSP does not mean it should load before the visitor's choice. Conversely, granting consent cannot override an enforced CSP. Test fresh, accepted and rejected consent states with the intended policy active.
Roll out policy changes without hiding existing enforcement
Use a report-only policy to evaluate a candidate policy before enforcing it broadly. It records candidate violations rather than blocking them; an existing enforced policy continues to apply. Do not assume that adding Report-Only disables the header already protecting the site.
Reports and browser evidence can contain URLs or other sensitive context. Configure a trusted collection endpoint and a retention policy, and avoid collecting code samples unnecessarily. MDN's CSP guide (opens in a new tab) discusses report-only deployment and reporting.
Repeat the failing interaction after the change. Confirm that the intended script runs, its required requests succeed and an unintended inline handler is still blocked when that is your policy. Check a fresh response rather than a tab whose document predates the change.
Fix the dependency contract, not just the console
Blanket permission for all HTTPS sources or unsafe inline execution can make the error disappear while broadening trust far beyond the dependency you intended. Likewise, disabling CSP globally can conceal which layer was wrong.
Keep a small dependency inventory: purpose, owner, required origins, consent category and expected initialization point. Browser JavaScript monitoring can expose a blocked integration after a release, but the repair should be a reviewed change to that contract. A clean console matters only if the intended feature works and the chosen protection remains in place.
Catch problems after your next deployment
Monitor availability, browser JavaScript errors and visual changes with DeployWatchers.