Content Security Policy (CSP) is one of the most powerful mechanisms for protecting web applications against cross-site scripting (XSS) attacks. Unlike input filtering, CSP works at the browser level: you explicitly specify which sources are allowed to load scripts, styles, images, and other resources from. Any attempt to load content from an unapproved source is blocked before it can run.
Why you need CSP
XSS attacks remain among the top OWASP vulnerabilities. An attacker can inject a malicious script through comments, feedback forms, URL parameters, or vulnerabilities in third-party libraries. CSP does not fix a vulnerability in your code, but it prevents the injected script from running—the browser simply refuses to load it.
CSP also protects against:
- Loading resources from compromised CDNs
- Injecting scripts through vulnerabilities in plugins
- Data URI injection in attributes
You can check your current CSP configuration and other security headers using the security headers analyzer and CSP analyzer on rechecker.ru.
Main CSP directives
script-src and style-src
The script-src and style-src directives control the sources of JavaScript and CSS. Unless 'unsafe-inline' is explicitly specified, inline scripts and styles are blocked.
Content-Security-Policy: script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'| Keyword | Description |
|---|---|
'self' | Only from the same origin (scheme + domain + port) |
'none' | Block all sources |
'unsafe-inline' | Allow inline scripts/styles (reduces protection) |
'unsafe-eval' | Allow eval(), new Function() (avoid) |
'strict-dynamic' | Scripts loaded by a trusted script are considered trusted |
default-src
The default directive for all resource types when a specific directive is not specified.
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.comimg-src, font-src, connect-src
img-src— image sourcesfont-src— fonts (Google Fonts, self-hosted)connect-src— XHR, fetch, WebSocket, EventSource
Content-Security-Policy: img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.comframe-ancestors
Controls who can embed the page in an iframe. It replaces X-Frame-Options with more extensive capabilities.
Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.comReport-Only mode
Before enabling a blocking policy, use Content-Security-Policy-Report-Only. The browser will send violation reports but will not block content. For more on testing a policy without blocking content, see CSP Report-Only: how to test a policy without blocking content.
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reportA modern option is report-to with the Reporting API:
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"/csp-report"}]}Configuration examples
Minimal policy for a static website
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'Website with Google Analytics and reCAPTCHA
Content-Security-Policy: default-src 'self'; script-src 'self' https://www.googletagmanager.com https://www.google.com https://www.gstatic.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://www.google-analytics.comSPA with a CDN and API
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; connect-src 'self' https://api.example.com wss://realtime.example.com; img-src 'self' data: https:; font-src 'self' https://cdn.jsdelivr.netNonce and hash for inline scripts
Instead of 'unsafe-inline', use a nonce—a one-time token generated by the server for each request:
<script nonce="r4nd0mV4lu3">
// Этот скрипт выполнится, если nonce совпадает с CSP
console.log('Authorized inline script');
</script>Content-Security-Policy: script-src 'self' 'nonce-r4nd0mV4lu3'An alternative is a hash: SHA-256 of the script content. Suitable for static content.
Deployment in Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;Deployment in Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"Deployment in Node.js (Express)
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"
);
next();
});Common mistakes
- An overly broad
img-src 'self' https:allows any HTTPS images. Specify specific domains. 'unsafe-inline'in script-src reduces XSS protection to a minimum. Use a nonce or hash.- Forgetting connect-src can cause API requests and WebSockets to be blocked.
- Not testing in Report-Only mode—a blocking policy can break functionality.
Step-by-step deployment plan
- Make a list of all external resources (CDNs, analytics, widgets).
- Enable the policy in Report-Only mode with
report-uriorreport-to. - Collect reports for 1–2 weeks and analyze violations.
- Add legitimate sources to the directives.
- Replace inline scripts with external ones or add a nonce/hash.
- Switch to blocking mode
Content-Security-Policy. - Monitor reports for new violations.
For a complete guide to configuring CSP, covering all directives and edge cases, see Content Security Policy: a complete configuration guide.