---
title: Content Security Policy Explained: what CSP actually stops
description: Your site works perfectly and still runs any script it's told to, from anywhere on the internet. CSP is the list that says which ones you actually meant — here's what it prevents, and how to switch it on without breaking your own site.
canonical: https://watchfor.io/blog/content-security-policy-explained
---

[All posts](/blog) [Security](/blog/category/security) Sep 08, 2026 · 6 min read · WatchFor Team

# Content Security Policy Explained: what CSP actually stops

Your site works perfectly and still runs any script it's told to, from anywhere on the internet. CSP is the list that says which ones you actually meant — here's what it prevents, and how to switch it on without breaking your own site.

Here's the uncomfortable default nobody mentions: a browser will run any script your page asks for, from anywhere, no questions asked. It has no idea which scripts you wrote, which ones your analytics vendor added, and which one an attacker slipped in an hour ago. They all look identical from the inside.

That's not a bug in your site. It's how the web works. Content Security Policy is how you opt out of it.

## Why this matters on a site that works fine

Every check in a [website audit](/smart-website-audit) can be green and this can still be missing, because CSP protects against something that hasn't happened yet. It doesn't make pages load. It doesn't fix errors. It's a seatbelt: irrelevant right up until the moment it isn't.

The thing it protects against is someone else's JavaScript running on your page with all of your privileges . Not "next to" your code — as your code. It can read anything on the page, including what a customer is typing into a form, and send it anywhere.

## How other people's code ends up on your page

You don't have to be careless for this to happen:

- A cross-site scripting (XSS) bug. A comment box, a search field, a URL parameter that gets printed back onto the page without being escaped. Someone submits a script tag instead of a name.

- A third-party script you deliberately added. Analytics, chat widgets, A/B testing, ad tags, payment iframes. Every one is a live code feed from a company you don't control, and it can change at any time without telling you.

- That vendor being compromised. This is the one that catches good teams. You didn't change anything; the script you've loaded for two years started doing something new one Tuesday.

- A dependency in your build. A package deep in your tree gets taken over and ships a new version. Your code is fine. Your bundle isn't.

The card-skimming attacks you read about — a checkout page that quietly copies card numbers as they're typed — are almost always one of the last three. The site looked perfectly normal the whole time. It had to: the point was that nobody noticed.

## What a Content Security Policy does

A CSP is a response header listing which sources of content the browser is allowed to load and run. Anything not on the list is refused — before it executes.

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none'; frame-ancestors 'self'; base-uri 'self'

In plain words: load things from my own domain; scripts may also come from that one CDN; never load plugins; nobody else may frame this page; nobody may rewrite my base URL.

Now the injected script from the comment box doesn't run, because it isn't from an allowed source. The attacker's exfiltration request doesn't leave, because the destination isn't on the list either. The bug is still there — the damage isn't.

## The directives worth knowing

Directive What it controls

default-src The fallback for everything you don't name explicitly

script-src Where JavaScript may come from — the important one

style-src Where CSS may come from

img-src Where images may load from

connect-src Where the page may send fetch /XHR/WebSocket requests

frame-ancestors Who may embed this page (see [clickjacking](/blog/clickjacking-explained))

object-src Flash and other plugins — set to 'none' , always

base-uri Stops an injected <base> tag redirecting every relative URL

## The part that makes people give up

CSP has a reputation for breaking sites, and it's earned. The usual cause is inline scripts — code written directly in the HTML rather than loaded from a file:

<button onclick = " doThing () " > Click </button>
<script> window . dataLayer = [] ;</script>
A strict policy blocks both, because the browser can't tell your inline script from an injected one. That's the entire point — and it's also why switching CSP on blindly can take a working site down.

The two honest ways out:

- Nonces. Generate a random value per request, put it on your own script tags ( <script nonce="r4nd0m"> ) and in the header ( script-src 'nonce-r4nd0m' ). Injected scripts can't guess it.

- Hashes. List the SHA-256 hash of each inline block you allow. Fine when they never change.

The way out that isn't: script-src 'unsafe-inline' . It's valid, plenty of sites use it, and it switches off most of the protection you came for.

## How to roll it out without a bad afternoon

There's a report-only mode, and it exists precisely so you never have to guess:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

The browser enforces nothing and tells you what it would have blocked. So:

- Ship the report-only header with the policy you think you want.

- Leave it for a week of real traffic. Read the reports.

- You will find things you forgot: an old marketing tag, a font host, a payment iframe.

- Add what's legitimate. Remove what isn't — you've just found scripts you didn't know you were running.

- When the reports go quiet, change the header name to the enforcing one.

Do the report-only step. Every "CSP broke our site" story is someone who skipped it. A week of listening costs nothing and tells you exactly what your site really loads — which is almost never what you assumed.

## What CSP does not do

Honesty matters more than a longer list of benefits:

- It doesn't fix the underlying bug. An XSS hole is still a hole; CSP contains what an attacker can do with it. Fix the escaping too.

- It doesn't protect the server. Anything happening in your backend is untouched.

- It doesn't help against a malicious script from an allowed source. If your CDN is on the list and your CDN is compromised, CSP shrugs. Subresource Integrity ( integrity="sha384-…" ) is the tool for that.

- Old browsers ignore parts of it. It's a strong extra layer, never the only one.

## Keeping it there

A CSP is one line in a config file, which means it can vanish in one deploy — a reverse proxy rebuilt, a framework upgraded, a header stripped by a new CDN rule. Nothing breaks when it disappears. That's the problem: the site looks exactly the same, and nobody finds out for months.

Check what your site sends right now with the free [HTTP header checker](/http-header-checker), or let the [Smart Website Checker](/smart-website-audit) look at every security header at once. If you want it watched rather than remembered, an [API monitor](/docs/monitors/api) can assert the header is present on every check and alert you the moment it isn't.

## The bottom line

In one line

What A header listing which sources of scripts, styles and connections are allowed.

Why Stops injected or hijacked JavaScript running with your site's privileges.

Danger without it One XSS bug or one compromised vendor script reads everything your users type.

Start with Content-Security-Policy-Report-Only , for a week, on real traffic.

Avoid 'unsafe-inline' in script-src — it undoes most of the benefit.

CSP is the one header on the list that takes real work. It's also the one that turns a serious incident into a blocked request in a console log. Start in report-only mode this week; you'll learn what your site actually loads either way.

Related: [Clickjacking explained](/blog/clickjacking-explained), [MIME sniffing and nosniff](/blog/mime-sniffing-explained), [HSTS explained](/blog/hsts-explained), [Mixed content warnings](/blog/mixed-content-warnings).

[#security](/blog/tag/security)[#web](/blog/tag/web)

## Monitor your website & APIs

Continuous HTTP/HTTPS checks with status-code, latency and content assertions — plus instant alerts when something breaks.

[Learn more](/http-monitoring)[Start free](/auth/sign-up)

Share this article

---

Canonical page: https://watchfor.io/blog/content-security-policy-explained · Site guide: https://watchfor.io/llms.txt
