Content Security Policy is the most effective defense against XSS attacks, and also the security header most likely to break your site. The gap between 'add a CSP header' and 'add a CSP header that works' is where most teams give up.
Start with report-only mode
Never deploy a CSP in enforcement mode on the first try. Use `Content-Security-Policy-Report-Only` and monitor what gets flagged. You will discover inline scripts you forgot about, third-party resources loaded from unexpected origins, and legacy code that generates dynamic styles.
The minimum viable CSP
Start restrictive and loosen only when needed: `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'`.
The 'unsafe-inline' problem
Most real-world sites need `unsafe-inline` for styles because CSS-in-JS libraries and component frameworks generate inline styles. For scripts, avoid `unsafe-inline` entirely - use nonces or hashes instead. A CSP with `script-src 'unsafe-inline'` provides almost no XSS protection.
Third-party scripts are the wildcard
Analytics, chat widgets, A/B testing tools, and payment processors all inject scripts from their own domains. Each one needs an explicit entry in your CSP. Audit your third-party scripts quarterly - vendors change domains, add subdomains, and load additional resources without notice.
Testing and monitoring
Use Mozilla Observatory to scan your headers. Set up CSP violation reporting to catch issues before users do. And test every CSP change in staging with a real browser - automated tests often miss CSP violations because they don't execute JavaScript the same way.