A Content Security Policy tells the browser which sources a page may load scripts, styles, images and other resources from, which is the main defence in depth against cross-site scripting (XSS). It is sent as the Content-Security-Policy header, as a list of directives separated by semicolons. Build one with the CSP Header Generator and grade a live site with the Security Headers Analyzer. The fallback chains below are taken from the CSP Level 3 specification.
Fetch directives and their fallbacks
If a directive is missing, the browser uses the next one in its fallback chain; if none is present, the resource type is unrestricted.
| Directive | Controls | Falls back to |
|---|---|---|
default-src |
Fallback for every fetch directive below | nothing |
script-src |
JavaScript (elements, attributes, eval) |
default-src |
script-src-elem |
<script> elements only |
script-src → default-src |
script-src-attr |
Inline event handlers such as onclick |
script-src → default-src |
style-src |
Stylesheets and inline styles | default-src |
style-src-elem |
<style> and <link rel="stylesheet"> |
style-src → default-src |
style-src-attr |
style="..." attributes |
style-src → default-src |
img-src |
Images and favicons | default-src |
font-src |
Web fonts | default-src |
connect-src |
fetch, XHR, WebSocket, EventSource, sendBeacon |
default-src |
media-src |
<audio>, <video>, text tracks |
default-src |
object-src |
<object> and <embed> plugins |
default-src |
manifest-src |
Web app manifest | default-src |
child-src |
Frames and workers (when the two below are absent) | default-src |
frame-src |
<iframe> and <frame> content |
child-src → default-src |
worker-src |
Web, shared and service workers | child-src → script-src → default-src |
Directives with no fallback
These are not covered by default-src; if you leave them out, nothing is restricted.
| Directive | Controls | Typical value |
|---|---|---|
base-uri |
URLs allowed in <base href> |
'self' or 'none' |
form-action |
Where forms may submit | 'self' |
frame-ancestors |
Who may embed this page in a frame (clickjacking) | 'none' or 'self'; replaces X-Frame-Options |
sandbox |
Applies iframe-style sandbox restrictions to the page | sandbox allow-scripts |
upgrade-insecure-requests |
Rewrites http: subresource URLs to https: |
no value |
report-to |
Reporting API group that receives violation reports | a group name from Reporting-Endpoints |
report-uri |
URL that receives violation reports (deprecated, still widely supported) | /csp-report |
require-trusted-types-for |
Forces Trusted Types for DOM XSS sinks | 'script' |
trusted-types |
Names the allowed Trusted Types policies | policy names |
Source expressions
| Expression | Allows |
|---|---|
'none' |
Nothing (must be the only value) |
'self' |
The page's own origin (same scheme, host and port) |
https: |
Any URL with that scheme; data:, blob: and others work the same way |
example.com |
That host; *.example.com covers its subdomains, not the bare domain |
https://cdn.example.com/js/ |
That path prefix on that host |
'unsafe-inline' |
Inline <script>/<style> and event handlers; ignored when a nonce or hash is present |
'unsafe-eval' |
eval(), new Function() and string timers |
'wasm-unsafe-eval' |
Compiling WebAssembly, without allowing JavaScript eval |
'nonce-<base64>' |
Elements whose nonce attribute matches; generate a new random value per response |
'sha256-<base64>' |
Inline content whose hash matches (sha384- and sha512- also work) |
'strict-dynamic' |
Scripts loaded by an already trusted script; host and scheme lists are then ignored |
'unsafe-hashes' |
Lets hashes match event handler attributes and style attributes |
'report-sample' |
Includes the first 40 characters of the blocked code in reports |
Delivery rules
- Header or meta.
<meta http-equiv="Content-Security-Policy" content="...">works, butframe-ancestors,report-uriandsandboxare ignored there, and the report-only mode cannot be set by meta. - Report-only.
Content-Security-Policy-Report-Onlysends violation reports without blocking anything: deploy a new policy this way first. - Several policies. If a response carries two CSP headers, a resource must pass both; adding a policy can only tighten, never loosen.
- Check what is sent. Inspect a page's real response headers with the HTTP Headers Viewer.
Starter policies
| Goal | Policy |
|---|---|
| Lock down a static site | default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self' |
| Strict nonce-based scripts | script-src 'nonce-R4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none' |
| Allow a CDN and images from anywhere over HTTPS | default-src 'self'; script-src 'self' https://cdn.example.com; img-src 'self' https: data: |
| Only stop clickjacking | frame-ancestors 'self' |
| API that returns JSON only | default-src 'none'; frame-ancestors 'none' |
The nonce R4nd0m stands for a fresh unpredictable value generated for every response; a fixed nonce gives no protection.