// Security
Content Security Policy (CSP) Guide: Protect Your Site Without Breaking It
CSP is the most powerful web security header — and the most complex to implement. This guide shows you how to build a working CSP policy step by step without breaking your site.
In this article
What CSP Does and Why It's Worth the Effort
Content Security Policy tells browsers exactly which sources of content are allowed to load on your pages. A properly configured CSP makes XSS (cross-site scripting) attacks nearly impossible, even if an attacker manages to inject malicious code into your HTML.
The challenge: modern web pages load resources from dozens of sources — Google Fonts, analytics, ads, social embeds. CSP requires you to whitelist all of them or they'll break.
Start With Report-Only Mode
Never deploy CSP in enforcement mode first. Use Content-Security-Policy-Report-Only with a report-uri to see what would be blocked without actually breaking anything. After a few days of monitoring reports, you'll know exactly what your site needs to whitelist.
Set up reporting with a service like Report URI (free tier) or your own endpoint. This gives you real-browser data from actual user sessions.
Building Your CSP Policy
- default-src 'self': Start by only allowing your own domain
- script-src: Add every JavaScript source (CDNs, analytics, ads) — this is the critical one
- style-src: Add CSS sources including Google Fonts if used
- img-src: Images from your domain plus any CDNs or tracking pixels
- connect-src: APIs your JavaScript calls (analytics endpoints, Supabase, etc.)
- frame-src: For embedded videos (YouTube, Vimeo) if applicable
- font-src: For Google Fonts or other web font services
Common CSP Breaking Points
Inline scripts and styles break with CSP. If your site uses onclick= attributes or <style> tags, you'll need to move them to external files or use nonce-based allowances. Google Tag Manager requires specific CSP configuration to work correctly.
Test thoroughly after deployment using the browser console — CSP violations show as console errors with 'Refused to load...' messages.