Anay Pandya
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
- Learn how to configure Content Security Policy headers step by step. Prevent XSS attacks, data injection, and clickjacking with real-world examples.
- Includes copy-paste code examples and step-by-step instructions.
- Free automated scan available to verify your implementation.
What Is Content Security Policy?
Content Security Policy (CSP) is an HTTP check your security headers that tells browsers which sources of content are allowed to load on your page. It is the single most effective defense against Cross-Site Scripting (XSS) attacks.
<div class="zf-stat-callout" style="background:#0d1117;border:1px solid rgba(16,185,129,0.25);border-left:3px solid #10b981;border-radius:4px;padding:16px 20px;margin:24px 0"> <p style="margin:0 0 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.15em;color:#10b981;font-family:monospace">ZeriFlow Data — 12,400+ sites analyzed</p> <p style="margin:0;font-size:13px;color:#e2e8f0;line-height:1.6;font-family:monospace">In our analysis of 12,400+ sites scanned on ZeriFlow, 64% lack a Content-Security-Policy header — and of those that have one, 71% use 'unsafe-inline', negating XSS protection entirely.</p> </div>
Is your site actually secure?
Run a free check — 60 seconds
Without CSP, a browser will execute any JavaScript it encounters on your page, whether you put it there or an attacker injected it. With CSP, the browser blocks anything not explicitly allowed by your policy.
Why CSP Matters
XSS is the most common web vulnerability. It lets attackers: - Steal session cookies and hijack user accounts - Redirect users to phishing pages - Inject cryptominers that use your visitors' CPUs - Deface your website with malicious content - Exfiltrate data from forms and pages
CSP does not just detect XSS — it prevents it at the browser level.
CSP Basics
A CSP header looks like this:
Content-Security-Policy: directive1 source1 source2; directive2 source3;Key Directives
| Directive | Controls | Example |
|---|---|---|
default-src | Fallback for all resource types | 'self' |
script-src | JavaScript files | 'self' cdn.example.com |
style-src | CSS stylesheets | 'self' 'unsafe-inline' |
img-src | Images | 'self' data: *.cloudinary.com |
font-src | Web fonts | 'self' fonts.gstatic.com |
connect-src | AJAX, WebSocket, fetch | 'self' api.example.com |
frame-src | Iframes | 'none' |
object-src | Flash, Java applets | 'none' |
base-uri | Base URL for relative URLs | 'self' |
form-action | Form submission targets | 'self' |
Source Values
| Value | Meaning |
|---|---|
'self' | Same origin only |
'none' | Block everything |
'unsafe-inline' | Allow inline scripts/styles (weakens CSP) |
'unsafe-eval' | Allow eval() (weakens CSP) |
'nonce-abc123' | Allow specific inline scripts with matching nonce |
*.example.com | Allow from subdomains |
https: | Allow any HTTPS source |
data: | Allow data: URIs |
Step-by-Step Implementation
Step 1: Start with Report-Only Mode
Do not jump straight to enforcing CSP. Start by monitoring what would be blocked:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reportThis logs violations without blocking anything. Check your server logs or a CSP reporting service to see what would break.
Step 2: Build Your Base Policy
Start strict and add exceptions:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';Step 3: Add Your CDNs and Services
If you use Google Fonts, Analytics, Stripe, etc., add them:
Content-Security-Policy:
default-src 'self';
script-src 'self' js.stripe.com www.googletagmanager.com;
style-src 'self' 'unsafe-inline' fonts.googleapis.com;
img-src 'self' data: *.google-analytics.com;
font-src 'self' fonts.gstatic.com;
connect-src 'self' api.stripe.com *.google-analytics.com;
frame-src js.stripe.com;
object-src 'none';
base-uri 'self';Step 4: Handle Inline Scripts with Nonces
Instead of using 'unsafe-inline' (which weakens CSP), use nonces:
<!-- Server generates a unique nonce per request -->
<script nonce="a1b2c3d4e5">
console.log('This is allowed');
</script>Content-Security-Policy: script-src 'self' 'nonce-a1b2c3d4e5';The nonce must be cryptographically random and different on every page load.
Step 5: Switch to Enforcement
Once you have confirmed no legitimate resources are blocked, remove Report-Only:
Content-Security-Policy: default-src 'self'; ...Server Configuration Examples
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';" always;Apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';"Next.js (next.config.js)
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none';"
}
];Common Mistakes
- 1Using `'unsafe-inline'` and `'unsafe-eval'` everywhere — This basically disables CSP protection. Use nonces instead.
- 2Setting `default-src *` — This allows everything and defeats the purpose.
- 3Forgetting `object-src 'none'` — Flash and Java applets are attack vectors.
- 4Not testing in report-only mode first — You will break your site.
- 5Setting CSP once and forgetting — New third-party scripts need to be added to the policy.
Check Your CSP
Run a ZeriFlow scan to instantly check if your website has a Content Security Policy header and how well it is configured. The scan evaluates your CSP along with 80+ other security checks.
Conclusion
Content Security Policy is the most powerful browser-side defense against XSS. Start with report-only mode, build your policy gradually, use nonces instead of unsafe-inline, and test regularly. Your users' security depends on it.
Further Reading
- OWASP Content Security Policy Cheat Sheet
- RFC 6797 — HTTP Strict Transport Security (HSTS)
- OWASP Top 10 Web Application Security Risks
<!-- zf-internal-links -->
Related resources
Keep improving your website security
Related tools
Website Vulnerability Scanner
Run a broader website security audit across headers, TLS, DNS, cookies, SEO, and disclosure checks.
Security Headers Checker
Check CSP, HSTS, X-Frame-Options, and other response headers.
SSL Checker
Review TLS certificate, HTTPS, and transport security signals.
DMARC Checker
Validate email authentication records for domain spoofing protection.
CSP Checker
Review Content-Security-Policy coverage and common gaps.