Content-Security-Policy
response headersecurity
Restricts where a page may load scripts, styles, images and frames from — the main defence against cross-site scripting.
A CSP is an allow-list for your page. If an attacker manages to inject a <script> tag, the browser refuses to run it unless the policy allows its source. Modern policies use nonces or hashes rather than host lists, so only the scripts your server deliberately marked can run.
Syntax
Content-Security-Policy: <directive> <source> <source>; <directive> …
Defined in W3C — Content Security Policy Level 3.
Values
| default-src | fallback for the other fetch directives |
|---|---|
| script-src | where scripts may come from ('self', nonces, hashes, hosts) |
| style-src | where stylesheets and inline styles may come from |
| img-src | where images may come from |
| connect-src | where fetch(), XHR and WebSockets may connect |
| frame-ancestors | which sites may embed this page in a frame |
| object-src | plugins; set to 'none' |
| base-uri | restricts the <base> element |
| form-action | where forms may submit |
| report-to / report-uri | where the browser reports violations |
Examples
Content-Security-Policy: default-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'
a strict starting point for a site that serves everything itself
Content-Security-Policy: script-src 'nonce-r4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none'
a nonce-based policy: only scripts carrying this response's nonce run
Good to know
- Roll out with
Content-Security-Policy-Report-Onlyfirst: violations are reported, nothing breaks. - Avoid
'unsafe-inline'for scripts — it undoes most of CSP's protection. Use nonces or hashes. - A nonce must be random and new on every response; a fixed nonce protects nothing.
frame-ancestors 'none'replacesX-Frame-Options: DENYin modern browsers.
Further reading: Content Security Policy guide · Security headers checklist · Security headers generator
How to set Content-Security-Policy
res.set("Content-Security-Policy", "default-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'");module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: [{ key: "Content-Security-Policy", value: "default-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'" }],
},
];
},
};add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'" always;
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'"
Check it
Fetch only the response headers and filter for Content-Security-Policy. In the browser, it's under Response Headers in DevTools → Network.
curl -sI https://example.com/ | grep -i '^content-security-policy:'
To compare response headers across many URLs at once, use the HTTP Status Checker.
Frequently asked questions
Why does my CSP break inline scripts and styles?
Inline code is blocked unless the policy allows it with a nonce, a hash, or 'unsafe-inline'. Move scripts into files, or add a per-response nonce to both the policy and the script tags.
Can I set CSP in a meta tag?
Yes, with <meta http-equiv="Content-Security-Policy">, but frame-ancestors, report-uri and sandbox don't work there. Prefer the header.