Content-Security-Policy for people who do not write CSPs
The one-sentence version
A Content-Security-Policy is a doorman for your page. You give the browser a list of sources you trust — your own domain, a specific CDN, an analytics provider — and the browser refuses to load scripts, styles or frames from anywhere else. If an attacker manages to inject a <script> that points somewhere not on your list, it simply does not run. That is the whole value: it neutralizes the most common cross-site-scripting payloads by default.
Why XSS is the thing it stops
Cross-site scripting is when an attacker gets their JavaScript to execute in your users' browsers — to steal sessions, read data, or act as the user. Most other defenses try to stop the injection from happening. CSP takes the opposite angle: it assumes something might slip through and makes the injected script useless, because it is not from an allowed source. Defense-in-depth, and it is the layer that saves you on a bad day.
The trap: unsafe-inline
Here is the part that catches almost everyone. Inline scripts — JavaScript written directly in the HTML rather than loaded from a file — are convenient, and frameworks lean on them. To allow them, people add unsafe-inline to their policy. But CSP cannot tell your inline script from an attacker's injected inline script, so unsafe-inline permits both. A policy with unsafe-inline in script-src looks like a CSP and provides little of the protection. It is the single most common real-world CSP finding for exactly this reason.
How to roll one out without breaking everything
A real CSP can block legitimate scripts if you guess wrong, so do it in stages:
- Start in report-only mode. Use
Content-Security-Policy-Report-Onlywith a reporting endpoint. The policy is not enforced — you just collect what would have been blocked. This shows you everything your site actually loads. - Build the allowlist from reality. Add the legitimate sources the reports reveal, rather than guessing up front.
- Replace inline scripts with nonces or hashes. Instead of
unsafe-inline, give each legitimate inline script a one-time nonce the policy trusts, so attacker-injected inline scripts still fail. - Switch to enforcing and drop
unsafe-inline. Once report-only is quiet, enforce the policy for real.
A reasonable starting point
A solid policy is restrictive by default and opens up only what you need: lock default-src to your own origin, allow scripts only from yourself and your nonces, and add the specific third parties you actually use. Tight first, loosened deliberately — never the other way around.
Check what your CSP really does
A CSP can look protective and not be, which is why it is worth verifying rather than assuming. An automated scan reads your policy and flags the weakening pieces like unsafe-inline, plus a missing CSP entirely. Scan your site and see whether your policy actually holds.