Content Security Policy (CSP) Explained

By Kalenfy · Updated 27 June 2026 · 7 min read

Content Security Policy (CSP) Explained

A Content Security Policy (CSP) is an HTTP response header that tells browsers exactly which sources of content — scripts, stylesheets, images, fonts — are allowed to load on your page. Anything not on the approved list gets blocked before it executes. It's one of the most effective browser-level defences against cross-site scripting (XSS) and data-injection attacks, and one of the most commonly missing security headers.

Why CSP exists

Without a CSP, a browser will run any JavaScript it finds on a page — whether you put it there or an attacker injected it. XSS attacks exploit exactly this: the attacker slips a script into your page and the browser executes it with full access to the user's session, cookies and DOM. CSP breaks that chain at the source. If the script's origin isn't in your policy, it never runs — regardless of how it got onto the page.

How CSP works

You deliver a CSP via the Content-Security-Policy HTTP response header (preferred) or a <meta http-equiv> tag. The value is a whitelist of directives, each controlling a different resource type:

A minimal CSP example

Here's a realistic policy for a site that loads scripts and fonts from a CDN:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' cdn.example.com;
  style-src 'self' fonts.googleapis.com;
  img-src 'self' data: images.example.com;
  font-src 'self' fonts.gstatic.com;
  frame-ancestors 'none'

Reading it: only your own domain and the listed third parties may supply content. Anything else — including any injected inline script — is blocked outright.

The two most dangerous CSP keywords: 'unsafe-inline' and 'unsafe-eval'

The single most common CSP mistake is adding 'unsafe-inline' to script-src. This re-enables all inline <script> tags and event handlers — exactly the vectors XSS injects into — and wipes out most of the protection. If you genuinely need inline scripts, use nonces instead: generate a random per-request value, put it in the header (script-src 'nonce-xyz123'), and add nonce="xyz123" to the <script> tags you wrote. Attackers can't predict your nonce, so injected scripts still get blocked.

'unsafe-eval' is equally risky: it allows eval(), Function(), and similar constructs that turn strings into executable code — a classic XSS payload delivery mechanism. Avoid it unless a library you depend on has no alternative.

Report-Only: how to test a CSP without breaking your site

Deploying a strict CSP to a live site cold-turkey risks breaking your own functionality. The safe approach is to start with Content-Security-Policy-Report-Only instead of enforcing. In report-only mode, violations are logged (to a URL you set with the report-to directive) but nothing is actually blocked. You can see exactly what would have been blocked, fix your allowlist, and switch to enforcement only when you're confident. Major browsers support this natively — no third-party tooling required.

What CSP can't protect on its own

CSP is a browser-side control. It won't stop:

Use CSP as one layer in a stack that also includes HTTPS, strict Strict-Transport-Security (HSTS), X-Content-Type-Options, Referrer-Policy, input validation, and regular scanning.

How to check if your site has a CSP

In Chrome DevTools: open the Network tab, click any page request, then check the Response Headers panel for Content-Security-Policy. If it's absent, you have no CSP. If it's present, look for 'unsafe-inline' or 'unsafe-eval' in script-src — either weakens it significantly.

The faster route is a passive scan that checks for the presence and strength of your CSP alongside your other security headers at once — and grades your overall posture in a single report.

FAQ

Can I use a CSP with WordPress or a CMS?

Yes, but it's harder because CMSes often rely on inline scripts. Start with report-only mode to find what you'd break, then progressively tighten. Many themes and plugins will need nonces or minor updates.

Does a <meta> tag CSP work as well as the header?

Mostly, but a <meta> tag can't use frame-ancestors or report-to, and it can be bypassed if an attacker can inject content before it in the <head>. Use the HTTP header wherever your server lets you.

What's a CSP nonce?

A nonce (number used once) is a random value you generate per page load, include in the header (script-src 'nonce-RANDOM'), and add as an attribute to legitimate <script nonce="RANDOM"> tags. Only scripts with that exact nonce attribute execute — attackers can't know it in advance.

Not sure if your site has a CSP or whether it's doing anything useful? Scan your domain free — we check for CSP presence alongside your other security headers and give you a plain-English grade.

Check your own domain — free

Kalenfy runs a passive scan of your SPF, DKIM, DMARC, DNSSEC, CAA and more, then gives you a downloadable PDF report with exact fixes. You see your grade first — no email needed to view it.

Scan my site free

Related guides