← The ten steps · Step 3 of 10 · 5 minutes
Read a violation report
The reports are the course material. Everything after this step is about acting on what they tell you.
What arrives
When a policy is violated the browser POSTs a small JSON document to your report-uri
endpoint:
{
"csp-report": {
"document-uri": "https://example.com/checkout",
"referrer": "",
"violated-directive": "script-src-elem",
"effective-directive": "script-src-elem",
"original-policy": "default-src 'self'; report-uri /csp-reports",
"disposition": "report",
"blocked-uri": "https://widget.example.net/chat.js",
"status-code": 200
}
}
The four fields that matter
blocked-uri
What the page tried to load. This is the field you act on. It may also be one of a few keywords rather
than a URL: inline (an inline script or style), eval (a string compiled as
code), or data (a data: URL).
violated-directive / effective-directive
Which rule caught it. Note that browsers report the specific directive that applied —
script-src-elem rather than script-src — even when your policy only names the
broader one. That is not an error; it is telling you precisely which mechanism was involved.
document-uri
Which page it happened on. This is what turns a list of violations into a map of your own site, and it is usually the field that reveals the pages nobody remembered existed.
disposition
"report" means report-only mode — nothing was actually blocked. "enforce"
means it was. Check this field before you panic about a spike.
Most of your reports will be noise
This surprises everyone the first time. A large share of violations on any public site come from browser extensions injecting scripts into your pages, from ISPs and antivirus products rewriting content, and from in-app browsers adding their own instrumentation. Common tells:
blocked-uriofchrome-extension://,moz-extension://orsafari-extension://- Domains you have never heard of appearing on every page, in tiny numbers, from a handful of users
- Violations for scripts that do not exist anywhere in your codebase
You cannot stop these — the extension runs in the user's browser, not yours — and you should not add them to your policy. Filter them out and move on. This is the main reason a hosted collector earns its keep: separating signal from extension noise at volume is tedious, and it is most of the work.
What you are looking for
Real findings look different. They appear consistently, on specific pages, across many users, pointing at origins you half-recognise: the tag manager, the font host, the A/B testing tool, the payment provider. Each one is a decision:
- Do I want this? If you cannot explain what it is, that is a finding in itself — you have just discovered a third party nobody documented.
- Does it need to be in the policy, or should it go away? A surprising number of these are dead tags nobody removed.
- If it stays, what is the narrowest way to allow it? Step 6 is about making that answer "a nonce" rather than "a growing list of hosts".
The reporting endpoint is a control, not a leak
You will occasionally read that CSP reporting is a data-exfiltration channel. The mechanism is real —
a report body names the URL that was blocked, so a policy pointed at an attacker's endpoint would tell
them things. But that requires the attacker to choose your report-uri, and if they can
rewrite your response headers you have lost already. Pointed at an endpoint you control, reporting is how
you find problems before someone else does.
You can do this now
Look at whatever your report-only policy from step 2 has collected. Find one violation that is not extension noise. That URL is a third party running on your site — do you know what it is?