Content Security Policy (CSP) is a W3C standard for protection against XSS and content injection. A policy specifies which sources are allowed to load scripts, styles, images, fonts, and other resources. The browser blocks anything that does not comply with the rules.
You can check and debug CSP using CSP Analyzer and Security Headers on rechecker.ru.
CSP architecture
CSP is set using the Content-Security-Policy header or a meta tag:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">The header takes precedence over the meta tag. If both are present, both are applied (the restrictions are combined).
For a brief introduction to configuration, see Content Security Policy (CSP): configuration and examples.
All CSP Level 3 directives
Resource loading controls
| Directive | Controls |
|---|---|
| default-src | Fallback for other *-src directives |
| script-src | JavaScript |
| style-src | CSS |
| img-src | Images |
| font-src | Fonts |
| connect-src | XHR, fetch, WebSocket, EventSource |
| media-src | audio, video |
| object-src | object, embed, applet |
| frame-src | iframe, frame |
| worker-src | Web Workers, Service Workers |
| manifest-src | Web App Manifest |
| base-uri | Restricts the base tag |
| form-action | Where forms can be submitted |
Reporting and embedding controls
| Directive | Purpose |
|---|---|
| report-uri | URL for sending reports (deprecated) |
| report-to | Report API group name |
| frame-ancestors | Who can embed the page in an iframe |
| sandbox | Restrictions for an iframe |
Source keywords
| Keyword | Description |
|---|---|
| 'none' | Disallow all sources |
| 'self' | Same origin only |
| 'unsafe-inline' | Inline scripts and styles (weakens protection) |
| 'unsafe-eval' | eval(), new Function(), setTimeout(string) |
| 'strict-dynamic' | Trusted scripts can load other scripts |
| 'unsafe-hashes' | Allow hashes for event handlers (dangerous) |
Source schemes
https://cdn.example.com — конкретный домен
https://*.example.com — все поддомены
https: — любые HTTPS (осторожно)
data: — data: URI
blob: — blob: URI
'self' — тот же originNonce
A nonce is a random value that changes with each request. It allows specific inline scripts and styles.
Server:
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy', `script-src 'self' 'nonce-${nonce}'`);
// Передать nonce в HTML
res.send(`<script nonce="${nonce}">...</script>`);HTML:
<script nonce="r4nd0mV4lu3">console.log('OK');</script>A nonce must be cryptographically random and unique for each response.
Hash
A hash is SHA-256, SHA-384, or SHA-512 of the script/style content. It is suitable for static content.
<script>console.log('hello');</script>Hash of console.log('hello'); (including spaces and line breaks as they appear in HTML):
'sha256-2qX6...'Content-Security-Policy: script-src 'self' 'sha256-2qX6...'If the script changes, the hash must be recalculated.
strict-dynamic
With 'strict-dynamic', a script loaded through a trusted method (nonce or hash) can create and execute new scripts. This simplifies working with dynamic loaders (for example, Webpack).
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic'In modern browsers, 'unsafe-inline' is ignored when 'strict-dynamic' is present.
Report-Only and reports
For a non-blocking mode, see CSP Report-Only: how to test a policy without blocking.
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reportThe modern option using the Report API:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}Report format (report-uri):
{
"csp-report": {
"document-uri": "https://example.com/page",
"violated-directive": "script-src",
"blocked-uri": "https://evil.com/malicious.js",
"original-policy": "default-src 'self'"
}
}frame-ancestors
Restricts who can embed a page in an iframe. Replaces X-Frame-Options.
Content-Security-Policy: frame-ancestors 'self' https://trusted.comframe-ancestors 'none' — completely disables embedding.
base-uri
Restricts the <base> tag, which can change the base URL for relative links.
Content-Security-Policy: base-uri 'self'form-action
Restricts the URLs to which forms can be submitted.
Content-Security-Policy: form-action 'self' https://payment.example.comExamples for popular stacks
React (Create React App / Vite)
Scripts are usually bundled. A minimal policy:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.comWordPress
There are many inline scripts and third-party plugins. Report-Only is recommended first, followed by gradual tightening. 'unsafe-inline' is often needed for script-src before refactoring.
Next.js
// next.config.js
const cspHeader = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
`;Step-by-step implementation
- Make a list of all sources (CDNs, analytics, widgets).
- Enable Report-Only with report-uri.
- Collect reports for 1–2 weeks.
- Add legitimate sources and replace inline code with nonce/hash where possible.
- Enable the blocking policy.
- Set up report monitoring.
Debugging
In DevTools → Console, CSP blocks are displayed with details. In Network, requests to blocked resources are marked as (blocked:csp).
Online: CSP Analyzer — analyzes a policy and checks for common errors.