Content Security Policy (CSP) is one of the most effective defenses against Cross-Site Scripting (XSS) attacks. By telling the browser exactly which resources are allowed to load, CSP can prevent malicious code injection even when your application has vulnerabilities.
What is CSP and Why Does It Matter?
CSP is an HTTP response header that controls which resources the browser is allowed to load for a given page. Without CSP, a single XSS vulnerability could allow attackers to inject scripts, steal cookies, redirect users, or deface your site.
How CSP Works
When a browser receives a CSP header, it enforces the policy by blocking any resource that violates the rules:
Browser receives HTTP response:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Browser allows:
✅ <script src="/app.js"> (same origin = 'self')
✅ <script src="https://cdn.example.com/lib.js">
Browser blocks:
❌ <script src="https://evil.com/steal.js">
❌ <script>alert('XSS')</script> (inline = not allowed)All CSP Directives
| Directive | Controls | Example |
|---|---|---|
| default-src | Fallback for all resource types | 'self' |
| script-src | JavaScript files and inline scripts | 'self' https://cdn.example.com |
| style-src | CSS stylesheets and inline styles | 'self' 'unsafe-inline' |
| img-src | Images (img, favicon, CSS background) | 'self' data: https: |
| font-src | Web fonts (@font-face) | 'self' https://fonts.gstatic.com |
| connect-src | XHR, fetch, WebSocket, EventSource | 'self' https://api.example.com |
| media-src | Audio and video elements | 'self' |
| frame-src | Iframes and embedded frames | 'self' https://www.youtube.com |
| object-src | Plugins (Flash, Java applets) | 'none' |
| base-uri | Base element URLs | 'self' |
| form-action | Form submission targets | 'self' |
| frame-ancestors | Who can embed this page (anti-clickjacking) | 'self' |
| upgrade-insecure-requests | Upgrade HTTP requests to HTTPS | (no value needed) |
Source Values Explained
| Source | Meaning |
|---|---|
| 'self' | Same origin (protocol + host + port) |
| 'none' | Block all resources of this type |
| 'unsafe-inline' | Allow inline scripts/styles (reduces security!) |
| 'unsafe-eval' | Allow eval(), Function(), setTimeout(string) |
| 'strict-dynamic' | Trust scripts loaded by already-trusted scripts |
| 'nonce-abc123' | Allow specific inline script with matching nonce attribute |
| 'sha256-...' | Allow inline script matching this hash |
| https: | Allow any HTTPS resource |
| data: | Allow data: URIs (e.g., inline images) |
| blob: | Allow blob: URIs |
| *.example.com | Allow all subdomains of example.com |
Step-by-Step Implementation
Step 1: Start with Report-Only Mode
Deploy CSP in report-only mode first to identify violations without breaking your site:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reportStep 3: Build Your Policy
Create a policy based on your findings. Start strict and relax as needed:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requestsCommon CSP Configurations
Strict Policy (Maximum Security)
Content-Security-Policy:
default-src 'none';
script-src 'self';
style-src 'self';
img-src 'self';
font-src 'self';
connect-src 'self';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
object-src 'none';
upgrade-insecure-requestsModerate (Typical SPA)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com wss://ws.example.com;
frame-ancestors 'self'With Google Analytics
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com https://analytics.google.comImplementation Methods
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self'"Express.js
// Using helmet middleware (recommended)
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
}
}));
// Or manually
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'");
next();
});HTML Meta Tag
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">Common CSP Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| Using unsafe-inline for scripts | Defeats XSS protection | Use nonces or hashes instead |
| Using unsafe-eval | Allows eval() and similar | Refactor code to avoid eval() |
| Wildcard (*) in script-src | Allows scripts from any domain | Whitelist specific domains |
| Forgetting about inline styles | Blocks CSS-in-JS libraries | Add unsafe-inline to style-src or use nonces |
| Not testing in report-only first | Breaks site functionality | Always start with report-only mode |
| Missing base-uri directive | Allows base tag injection | Add base-uri 'self' |
FAQ
What attacks does CSP prevent?
CSP primarily prevents Cross-Site Scripting (XSS) attacks by controlling which scripts can execute. It also helps prevent clickjacking (via frame-ancestors), data injection, and mixed content issues.
Does CSP replace input validation?
No. CSP is a defense-in-depth measure. You should still validate and sanitize all user input. CSP acts as a safety net when other defenses fail.
How do I use CSP with inline scripts?
Use nonces (random tokens added to both the CSP header and script tags) or hashes (SHA-256 of the script content). Avoid unsafe-inline as it defeats the purpose of CSP.
Will CSP slow down my website?
No. CSP is enforced by the browser with negligible performance impact. The header itself is tiny. The only overhead is generating nonces if you use them, which is minimal.