Skip to content
← All guidesGUIDE · UPDATED 2026-09-24

What a Content-Security-Policy header actually does

How directives and fallbacks resolve, why a policy does not fix injection by itself, and how to roll one out safely.

A Content-Security-Policy (CSP) header tells the browser which sources may load scripts, styles, images, frames, and other resources on your pages. It is one of the few defences that limits the damage of an injection bug. It is also commonly deployed in a way that either breaks the site or provides almost no protection. This guide explains how policies are evaluated, how to write one, and how to roll it out without an outage.

What CSP protects against

The main threat is cross-site scripting (XSS): an attacker gets their own script to run on your page, often through unescaped user input, and then reads data, hijacks sessions, or performs actions as the user. A good CSP means an injected <script> tag or inline event handler is simply not executed, because its source is not on the list. CSP also controls who may frame your pages, which prevents clickjacking, and where forms may submit.

CSP does not fix injection. If your application concatenates user input into HTML, that bug remains. The policy only limits what the injected code can do. Treat it as defence in depth, and keep escaping, validation, and safe templating as the primary controls.

Directives and the default-src fallback

A policy is a semicolon-separated list of directives, each with a list of allowed sources:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'

default-src is the fallback for fetch directives that are not listed. Here, scripts, styles, fonts, and connections all fall back to 'self', the page’s own origin. Only images get an exception, for data: URLs. Three important directives do not fall back to default-src and must be set explicitly:

  • base-uri: stops an injected <base> tag from redirecting every relative URL on the page.
  • frame-ancestors: who may embed your page in a frame. It replaces the older X-Frame-Options header.
  • form-action: where forms may submit.

The CSP policy builder shows the resolved fallback for each directive while you edit, so you can see what a missing directive actually allows.

Source expressions

  • 'self': the page’s own origin (same scheme, host, and port).
  • https://cdn.example.com: a specific host. Paths and wildcards such as https://*.example.com are allowed.
  • data:, blob:: schemes. Avoid data: for scripts.
  • 'nonce-…': allows inline scripts carrying a matching random nonce attribute, generated fresh for every response.
  • 'sha256-…': allows an inline script whose exact content has this hash (the Base64 of its SHA-256 digest).
  • 'none': nothing at all. Use it for object-src, since plugins are obsolete.

'unsafe-inline' in script-src allows any inline script, including injected ones, which removes most of CSP’s protection against XSS. 'unsafe-eval' allows eval() and similar functions. Prefer nonces or hashes, and remove these keywords wherever possible.

Rolling out a policy safely

  1. Start with the Content-Security-Policy-Report-Only header. Violations are reported in the browser console, and to an endpoint if you configure reporting, but nothing is blocked.
  2. Browse every type of page, including login, checkout, and error pages, and note each violation.
  3. For each one, either remove the dependency, move inline code into a file, or add a deliberate, narrow exception.
  4. When reports are clean, switch to the enforcing header and keep monitoring. New features and third-party updates add new sources over time.

Third-party services

Analytics, advertising, chat widgets, payment forms, maps, and video embeds each need their own origins in script-src, connect-src, img-src, and frame-src. Their documentation usually lists them. Advertising is the hardest case. Ad networks load scripts and frames from many domains that change over time, so sites that show ads typically allow the network’s published domains and accept a broader policy for those directives. This site loads Google AdSense and its consent tool, and its policy lists the Google origins those services need, while keeping object-src and base-uri locked down.

Every third-party script you allow runs with the same power as your own code. The fewer you allow, the smaller the risk, so review the list whenever the policy grows.

Header vs. meta tag

A policy can also be set with <meta http-equiv="Content-Security-Policy">. That is useful on static hosts without header control, but frame-ancestors, reporting, and report-only mode only work in the HTTP header. Where you can, set CSP as a response header, for example in your server, CDN, or hosting platform’s headers file.

CSP works alongside other security headers: Strict-Transport-Security to force HTTPS, X-Content-Type-Options: nosniff, and a Referrer-Policy that limits what URLs are shared with other sites. A policy can be checked in the browser’s developer tools, where blocked resources appear as console errors naming the directive responsible.