In brief
CSP stands for Content Security Policy and is an HTTP response header that hands the browser a whitelist: "Load scripts only from these sources, images only from those, fonts only from these — and ignore everything else." If an attacker manages to inject foreign code into your website (XSS), the CSP prevents it from executing — the browser simply blocks the source.
Example header
Content-Security-Policy: default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
The most important directives
default-src— fallback for all directives not specified explicitly.'self'= your own domain only.script-src— sources for JavaScript.'unsafe-inline'must never appear here, otherwise the CSP is useless against XSS.style-src— sources for CSS.'unsafe-inline'is less critical here, but a code smell.img-src— images.data:allows Base64 images,https:allows images from any HTTPS source.connect-src— XHR/Fetch/WebSocket targets. Important for API calls to other domains.frame-ancestors— who is allowed to embed your site in an iframe?'none'= nobody. Replaces the older X-Frame-Options header.base-uri— prevents manipulation of<base>tags via XSS.form-action— where are forms allowed to submit? Default: any domain — which is an XSS risk.
Avoid unsafe-inline
A CSP with script-src 'unsafe-inline'
allows every inline script on the page — and therefore every script injected by an
attacker. The CSP becomes ineffective. Replacements:
- Nonces: every request gets a random nonce that appears both in the CSP and on the inline script tag. Only scripts with the matching nonce are executed.
- Hashes: list the SHA-256/384 hashes of your inline scripts in the CSP. Works for static content.
- External files: move inline scripts into separate .js files.
Rolling out a CSP
A CSP that is too strict will break features — tracking, embedded tweets, analytics, live chat widgets, Stripe buttons, Google Maps. Recommended order:
- Report-Only mode: first enable the CSP as
Content-Security-Policy-Report-Onlywithreport-toorreport-uri. Violations are reported but nothing is blocked. - Collect for 2–4 weeks: tools like Sentry, csp-report-collector or your own endpoint gather the reports.
- Curate the list: add all legitimate sources, ignore or fix illegitimate ones.
- Enforce: switch the header to
Content-Security-Policywithout-Report-Only.
Common mistakes
- 'unsafe-inline' in script-src: renders the CSP useless against XSS — the most common weakness.
- 'unsafe-eval': allows
eval(),new Function(),setTimeout(string)— should be avoided. - '*' as source: loads from any HTTPS server. Practically no protection.
- frame-ancestors missing: X-Frame-Options is deprecated, frame-ancestors is the modern variant. Set it to
'none'or'self'.