Why Content Security Policy is your first line of defense
CSP is not a firewall, it's a whitelist of what resources your app can load. Without CSP, any injected script (XSS) executes with your domain's permissions: access to cookies, localStorage, can make requests to your API. With CSP, even if an attacker injects <script>alert('hack')</script>, the browser blocks it if it doesn't meet the policy. It's defense in depth: you assume your code has bugs, but you limit the damage. CSP also prevents clickjacking with frame-ancestors, forces HTTPS with upgrade-insecure-requests, and blocks obsolete plugins with object-src 'none'. A well-configured policy reduces XSS risk by 90%.
Essential CSP directives and what they control
default-src: Fallback for everything you don't specify; start with 'self'. script-src: Where scripts can come from; avoid 'unsafe-inline' (allows XSS), use nonces or hashes. style-src: Same for CSS; third-party libraries often need 'unsafe-inline'. img-src: Image sources; data: allows base64, https: allows any HTTPS. connect-src: Allowed APIs and WebSockets; limit to your domains. frame-ancestors: Who can embed your page; 'none' prevents clickjacking. base-uri: Limits <base> tag (rare but real attack vector). form-action: Where forms can submit.
Migrating to CSP without breaking production
Start with Content-Security-Policy-Report-Only: logs violations without blocking anything. Configure report-uri /csp-violations and analyze what resources load (Google Fonts, CDNs, analytics). Add domains to whitelist gradually. Common findings: inline styles in React components, inline event handlers (onclick='...'), eval() in old libraries. Solutions: extract inline styles to files, use programmatic event listeners, replace libraries using eval. Once Report-Only shows no violations for 1 week, activate the real header. Use dynamic nonces (generate random per request) for critical inline scripts.
Common mistakes when configuring CSP
Using 'unsafe-inline' and 'unsafe-eval' by default: Negates 80% of CSP protection; only use if no alternative and you understand the risk. Forgetting 'self' in default-src: If you set default-src https:, your own scripts fail; always include 'self'. Not specifying protocol in domains: cdn.example.com allows HTTP and HTTPS; better https://cdn.example.com. Whitelist too broad: script-src https: allows any HTTPS, including compromised CDNs; be specific. Not testing in all browsers: Safari has CSP quirks; test before production. Not versioning policies: Keep your CSP in git; you know what changed if something breaks.