The fix, in one snippet
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m'; object-src 'none'
<script nonce="r4nd0m">…</script>
Illustrative values. Change the paths, hostnames and options to match your own site before using it.
The sections below explain what is tested, why it matters and the alternatives.
What we test
Scan.now parses every directive of the enforced Content-Security-Policy header (or the meta element) and evaluates the script-controlling ones. It flags: 'unsafe-inline' in script-src (or in default-src when script-src is absent) with no nonce or hash source, since browsers ignore 'unsafe-inline' only when a nonce or hash is also present; 'unsafe-eval'; the sources *, https:, http: and data: in script-src; a missing object-src with no default-src fallback; and a missing base-uri. It notes when 'strict-dynamic' is present (host sources are then ignored, which is intended), when only the deprecated report-uri is used for reporting, and when 'unsafe-inline' appears in style-src (informational, a far smaller risk). The check requires a policy to exist; a missing one is its own finding.
Why it matters
Each of these directives maps to a known bypass. 'unsafe-inline' permits exactly the injected inline <script> that CSP exists to stop, so a policy with it provides no XSS protection at all. 'unsafe-eval' allows eval(), new Function() and string arguments to setTimeout, which many exploitation gadgets rely on. A data: or wildcard script source lets an attacker load arbitrary code from a URL they control. Broad CDN allowlists are weaker than they look: Google researchers (Weichselbaum et al., CCS 2016) analysed policies from over a billion hostnames and found that around 95% of the allowlist-based ones could be bypassed, typically through JSONP endpoints or AngularJS copies hosted on the allowed CDN. Without object-src 'none', Flash-era plugin loading is still nominally permitted, and without base-uri, an injected <base> tag can redirect every relative script URL. See the CSP guide for the reasoning behind each directive.
How to fix it
Replace host allowlists and 'unsafe-inline' with per-response nonces plus 'strict-dynamic', and pin down the two directives that have no safe default:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic' https: 'unsafe-inline'; object-src 'none'; base-uri 'none'
The trailing https: 'unsafe-inline' is a deliberate fallback for browsers without 'strict-dynamic' support and is ignored by those that have it. Every inline script then needs the nonce:
<script nonce="{RANDOM}">initApp();</script>
Django can do this with the django-csp package; Rails has content_security_policy_nonce_generator; Express example in the CSP check. If the application cannot generate nonces, hashes work for static inline scripts:
script-src 'sha256-<base64 of the exact script text>' 'strict-dynamic'
Where nginx must add the header for a static site, use $request_id as the nonce and inject it into the HTML with sub_filter:
add_header Content-Security-Policy "script-src 'nonce-$request_id' 'strict-dynamic'; object-src 'none'; base-uri 'none'" always;
sub_filter_once off;
sub_filter 'nonce="NONCE"' 'nonce="$request_id"';
Remove 'unsafe-eval' by upgrading libraries that need it (old template engines, some charting libraries) or by moving their compilation step to build time. Google's CSP Evaluator gives an instant second opinion on a policy string.
Where this fits
Content-Security-Policy is weak is check 3 of 13 that the website vulnerability scanner runs under http security headers, ordered the way they are worth fixing. That ordering is the point: Fixing this one while the check above it still fails buys less than it looks like.
Fix this one first
Above it in the same category sits Clickjacking protection (X-Frame-Options / frame-ancestors) (medium), where the page can be embedded in a frame on any other site. An attacker who has that does not need this, so it is the better use of the same hour.
What fixing this still leaves open
Content-Security-Policy is weak closes one route in. Immediately below it: Content-Security-Policy missing, where the page is served without an enforced Content-Security-Policy; Cross-Origin-Opener-Policy, where the page does not set Cross-Origin-Opener-Policy, so windows it opens, or that open it, from other origins keep a reference to it and share its browsing context group; Permissions-Policy, where no Permissions-Policy header restricts powerful browser features.
Found in the same scan
The website vulnerability scanner reports this alongside checks from other categories that are at least as serious, including Certificate chain and hostname validation, where the certificate presented for this hostname did not validate: The chain does not reach a trusted root, an intermediate is missing, the name does not match, or the certificate is self-signed or expired, and Exposed .env configuration file, where a .env configuration file is served from the web root. A single run of website vulnerability scanner answers all of them at once.
The first few lines
You are a senior web engineer. I ran a security and SEO scanner against my site and it reported the finding below. Fix it properly rather than suppressing the symptom.
Finding: Content-Security-Policy is weak (medium severity)
Scanner check id: csp-weak-directives
The rest of this prompt names the pages and line numbers we found the problem on, the configuration to change, and the constraints a good answer has to respect. It is free, it just needs an account so the work is not scraped wholesale.
Sign in with GoogleSigning in is free and takes one click. We store your email address and nothing else.