Skip to main content

What is a CSP?

The Content Security Policy is the most important line of defense against Cross-Site Scripting on the modern web.

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:

  1. Report-Only mode: first enable the CSP as Content-Security-Policy-Report-Only with report-to or report-uri. Violations are reported but nothing is blocked.
  2. Collect for 2–4 weeks: tools like Sentry, csp-report-collector or your own endpoint gather the reports.
  3. Curate the list: add all legitimate sources, ignore or fix illegitimate ones.
  4. Enforce: switch the header to Content-Security-Policy without -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'.

Check it yourself: HTTP-Header

HSTS, CSP, X-Frame-Options & Co. Enter a domain and see in seconds how your CSP is doing.

Also in the glossary