CSP is the most powerful browser-level defense against XSS attacks. At Google, every application has a strict CSP.
What is CSP?
A Content Security Policy tells the browser which resources are allowed to load and execute. If an attacker injects a script, the browser blocks it.
Setting CSP
// HTTP Header (preferred)
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'
// Meta tag (limited)
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">Directive Reference
| Directive | Controls | Example |
|---|---|---|
| default-src | Fallback for all | 'self' |
| script-src | JavaScript | 'self' 'nonce-abc' |
| style-src | CSS | 'self' 'unsafe-inline' |
| img-src | Images | 'self' data: https: |
| connect-src | Fetch/XHR/WS | 'self' https://api.example.com |
| font-src | Fonts | 'self' https://fonts.gstatic.com |
| frame-src | iframes | 'none' |
| frame-ancestors | Who can frame you | 'none' (prevents clickjacking) |
Nonce-Based CSP (Recommended)
// Server generates random nonce per request
const nonce = crypto.randomBytes(16).toString("base64");
// Header
Content-Security-Policy: script-src 'nonce-${nonce}'
// HTML — only scripts with matching nonce execute
<script nonce="${nonce}">
// This runs ✅
console.log("Legitimate script");
</script>
<script>
// This is blocked ❌ (no nonce)
alert("XSS attack!");
</script>Strict CSP Template
Content-Security-Policy:
default-src 'none';
script-src 'nonce-{random}' 'strict-dynamic';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'none';
form-action 'self';
upgrade-insecure-requests;Report-Only Mode
// Test CSP without breaking anything
Content-Security-Policy-Report-Only:
default-src 'self';
report-uri /csp-violations;
// Violations are reported but not blocked
// Monitor for a week before enforcingCommon Pitfalls
- Never use
'unsafe-eval'— it defeats CSP's purpose 'unsafe-inline'for scripts is dangerous — use nonces instead'strict-dynamic'allows dynamically created scripts by trusted scripts- Always include
frame-ancestors 'none'to prevent clickjacking