Learn how to configure Content Security Policy headers step by step. Prevent XSS attacks, data injection, and clickjacking with real-world examples.
Anay Pandya
870 words
Anay Pandya
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
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>
Keep reading
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.
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.
A CSP header looks like this:
Content-Security-Policy: directive1 source1 source2; directive2 source3;| 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' |
| 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 |
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.
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';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';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.
Once you have confirmed no legitimate resources are blocked, remove Report-Only:
Content-Security-Policy: default-src 'self'; ...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;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';"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';"
}
];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.
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.
<!-- zf-internal-links -->
Related resources
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.
January 17, 2026
Cookie Security: Every Flag Developers Must Set in 2025
Monitor your security score
Schedule recurring scans and catch regressions after deployments.