Content-Security-Policy: directives, sources and reporting
Content-Security-Policy is an allowlist the browser enforces: each directive names the origins a page may load a given resource type from, and anything not listed is blocked before it runs.
At a glance
- Direction
- Response header, and a subset works in a meta element for pages where you cannot set headers.
- Defined in
- Content Security Policy Level 3 (W3C). Level 2 was the last Recommendation.
- When absent
- No restriction. The page may load scripts, styles, frames and images from anywhere.
- Fallback directive
- default-src, used by fetch directives that are not listed separately
- Not covered by default-src
- frame-ancestors, base-uri, form-action, report-uri and report-to
- Multiple headers
- Each policy is enforced separately, so two headers intersect rather than merge.
- In the headers checker
- Graded. It warns on unsafe-inline, unsafe-eval, a wildcard source, and a policy with no default-src or script-src.
In detail
A policy is a list of directives separated by semicolons, each naming a resource type and the sources allowed for it. Matching happens per request: the browser resolves the URL it is about to fetch, compares it against the source list for that directive, and refuses the fetch if nothing matches. Blocked resources appear in the console as a policy violation rather than a network error, which is the fastest way to tell the two apart.
default-src is the fallback for fetch directives you did not write, so default-src 'self' quietly covers script-src, img-src, style-src, connect-src and the rest. It does not cover frame-ancestors, base-uri or form-action, and those three carry real weight: frame-ancestors is what actually stops clickjacking, base-uri stops an injected base element rewriting every relative URL on the page, and form-action stops a form posting your fields to somebody else.
The unsafe-inline source allows every inline script and event handler attribute on the page, which puts you back roughly where you started against injected markup. The replacements are a per response nonce, where each script tag you control carries a nonce attribute matching the header, or a hash of the exact script body. A nonce must be unguessable and regenerated on every response, so it cannot live in a cached HTML file.
Roll a policy out with Content-Security-Policy-Report-Only first, collect violations for a week, then move the same string to the enforcing header. Two policies sent at once are intersected rather than combined, so an enforcing header plus a looser second header does not loosen anything: a request has to pass every policy that was sent.
Examples
Content-Security-Policy: default-src 'self'; img-src 'self' data:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'Content-Security-Policy: script-src 'nonce-r4nd0mV4lu3' 'strict-dynamic'
<script nonce="r4nd0mV4lu3">startApp();</script>The nonce must be freshly generated per response. Reusing one value makes it worthless.
Content-Security-Policy: default-src 'self'; upgrade-insecure-requests; report-to csp-endpointThings that catch people out
- A data: source in script-src re-enables a whole class of injection, because an attacker can encode a script directly in the URL. It is usually only safe in img-src and font-src.
- A policy with script-src but no object-src leaves plugin embeds allowed. Add object-src 'none' unless you truly need it.
- frame-ancestors is ignored in a meta element policy, so a page that sets CSP only through markup gets no clickjacking protection from it.
- The 'self' source does not include subdomains. api.example.com is a different source from example.com and needs listing.
Try it: HTTP Headers Checker
Check a live URL or paste headers you already have. It grades six security headers from A+ to F and lists every other header the response returned.
HTTP Headers Checker
LOCAL · Browser-OnlyView HTTP response headers for any URL
Enter any website URL to fetch and analyze its HTTP response headers. We'll automatically add https:// if needed.
Why Check Security Headers?
Security headers protect your website against common attacks like XSS, clickjacking, and MIME sniffing. They're set by your web server and cost nothing to implement. Missing security headers are one of the most common findings in security audits.
Related HTTP headers
All http headers →Content-Security-Policy-Report-Only: testing a policy safely
How the report-only CSP header measures a policy without blocking anything, where the violation reports go, and when to run it alongside an enforcing policy.
X-Frame-Options: DENY, SAMEORIGIN and clickjacking
What DENY and SAMEORIGIN do, why ALLOW-FROM never worked, and how CSP frame-ancestors supersedes this header while it is still worth sending.
X-XSS-Protection: a legacy header and why it is off
What the browser XSS auditor did, why Chrome and Edge removed it, why 0 is now the recommended value, and what to send in its place.
Reporting-Endpoints: where browser reports are sent
How Reporting-Endpoints names collectors for CSP violations and deprecation warnings, how it differs from the older Report-To header, and what arrives.
X-Content-Type-Options: nosniff and MIME sniffing
What nosniff stops a browser doing, why it is the safest security header to add, and the two kinds of resource it starts rejecting once you turn it on.
Referrer-Policy: controlling what the Referer header leaks
Every Referrer-Policy value compared, what modern browsers do when the header is missing, and why URLs with tokens in them leak to third parties without it.
Tools for this
- HTTP Headers Checker Free online HTTP headers checker - view HTTP response headers for any URL
- SEO Analyzer Free online SEO analyzer - analyze any website's SEO score and get actionable improvement tips