How to Build a Content Security Policy Without Breaking Your Site
CSP is the most powerful browser security feature and the most feared. Here's a step-by-step guide to building one that actually works.
Content Security Policy is the most effective defence against cross-site scripting (XSS), and the header most developers avoid implementing. The fear is justified — a poorly written CSP will break your site. But the approach is simple: start in report-only mode, monitor what would be blocked, and tighten the policy iteratively. Here's how.
What CSP actually does
CSP tells the browser which sources are trusted for each type of resource. If an attacker injects a script tag pointing to their malicious server, the browser checks the CSP, sees the source isn't whitelisted, and refuses to load it. Without CSP, the browser has no way to distinguish between scripts you intended to load and scripts an attacker injected.
Step 1: Start with report-only
The Content-Security-Policy-Report-Only header has the exact same syntax as the enforcing header, but it doesn't block anything. It just reports what would be blocked. Deploy this first and monitor the violations for a week to understand what your site actually loads.
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; report-uri /api/csp-report
Step 2: Understand the key directives
default-src is the fallback for any resource type you don't explicitly configure. Set it to 'self' so only your own domain is trusted by default. script-src controls JavaScript sources — this is the critical one for XSS prevention. style-src controls stylesheets. img-src controls images. connect-src controls fetch, XHR, and WebSocket endpoints. font-src controls web fonts. frame-src controls iframes.
Step 3: Whitelist your actual dependencies
Look at your CSP violation reports and add the legitimate sources. If you use Google Fonts, add fonts.googleapis.com to style-src and fonts.gstatic.com to font-src. If you use a CDN, add it to script-src. Be as specific as possible — whitelist exact domains, not wildcards. Each wildcard is a potential bypass.
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com;
The 'unsafe-inline' problem
Many sites need 'unsafe-inline' for style-src because CSS-in-JS frameworks and inline styles are everywhere. This is generally acceptable for styles. But 'unsafe-inline' in script-src largely defeats the purpose of CSP because it allows any inline script to execute — including injected ones. The alternatives are nonce-based CSP (add a random nonce to each script tag and the header) or hash-based CSP (hash each inline script and whitelist the hashes). Modern frameworks like Next.js support nonces out of the box.
# Nonce-based approach
Content-Security-Policy: script-src 'nonce-abc123random'
# In your HTML
<script nonce="abc123random">
// This will execute
</script>
# Injected scripts without the nonce are blocked
<script>alert('xss')</script> <!-- BLOCKED -->Step 4: Enforce and monitor
Once your report-only policy has been running for a week with no legitimate violations, switch from Content-Security-Policy-Report-Only to Content-Security-Policy. Keep the report-uri so you catch any new violations — these often surface when a team member adds a new third-party script without updating the CSP.
Generate your CSP
KrakenProbe scans your site and generates a recommended Content-Security-Policy based on the resources it finds, plus a report-only version you can deploy immediately. Run a free scan with AI analysis enabled to get your custom CSP.
Check your site now
Run a free security scan — 8 scanners check your site in seconds.
Scan your website