Skip to main content
Free with an account

CSP Report Endpoint

A Content-Security-Policy without a report endpoint is silent. It breaks things in your visitors' browsers and never tells you. Point report-uri at a collector, and find out before you enforce.

Why a crawl is not enough

A scanner sees what a scanner can reach: public pages, logged out, once. Real traffic includes the page behind the login, the checkout step, the A/B variant, the legacy widget on one forgotten URL, and whatever a marketing tag loaded this morning. Those are exactly the places a policy written from a crawl turns out to be too strict.

Browsers already know. Every time a policy blocks something, the browser is willing to post a short JSON report saying what and where. It just needs somewhere to send it.

How it works

  1. 1

    Add your domain

    A free account, then one monitor per site. Each gets its own secret collector URL — a long random token in the path, visible only to you.

  2. 2

    Point your policy at it

    Append report-uri to the policy you already serve, and add a Reporting-Endpoints header with report-to so newer browsers report too. Browsers that understand report-to ignore report-uri, so listing both covers everyone.

  3. 3

    Ship it in Report-Only mode

    Send the strict policy under Content-Security-Policy-Report-Only. Nothing is blocked. The browser tells you what it would have blocked, in visitors’ real sessions, on the pages a crawler never sees.

  4. 4

    Read what came back, and act

    Reports are folded to one row per day, directive and blocked origin, each with what it means and the fixes in order. When the only thing left is extension noise, rename the header and start enforcing.

What you add to your site

Two headers. Your collector URL replaces the token below; you get the real one when you create the monitor.

Content-Security-Policy-Report-Only:
  default-src 'self';
  object-src 'none';
  base-uri 'self';
  report-uri https://intodns.ai/api/csp-report/u/<your-token>;
  report-to csp-endpoint

Reporting-Endpoints:
  csp-endpoint="https://intodns.ai/api/csp-report/u/<your-token>"

Keep it on the Report-Only header name until the reports are quiet. Under that name a policy cannot break anything, which is the whole point of starting there.

What comes back

Browsers send one report per blocked resource per page view, so a busy site produces tens of thousands a day that say the same four things. They arrive here folded to one row per day, directive and origin, each carrying what to do about it.

DirectiveBlocked originCountWhat to do
script-srcinline8,891
criticalMove the code into a .js file

Nearly every XSS lands as inline script, so a policy that allows it stops almost nothing. No button here adds ’unsafe-inline’ to a script directive — that is the one fix worth refusing.

script-srchttps://cdn.jsdelivr.net1,204
highHost the file yourself

Recognised as the jsDelivr CDN. Allowing a third party in a script directive lets that origin run code in your pages after any future change on their side.

img-srcdata312
lowAdd data: to img-src

Ordinary build output — small images inlined to save a request. Rendered, not executed, so allowing it costs little.

style-srcchrome-extension://…97
lowNo action

A visitor’s extension injecting into your page. Not your site, and not something to widen a directive for.

Illustrative rows, to show the shape of the output.

Then it writes the policy

Once a representative period has passed, the monitor can synthesize a policy from what your visitors actually loaded, optionally combined with a fresh crawl. It starts locked down — default-src 'self', object-src 'none', base-uri 'self', frame-ancestors 'self' — and adds only origins that were really observed.

Origins seen once are still included, but called out for review: a single sighting is as likely to be somebody's browser extension as a real dependency. A weekly digest tells you when something new shows up, which is how you find out that a new marketing tag went live.

Get your endpoint

Free account, one monitor per domain, your own collector URL. The reports are yours — nobody else can read them.

Create a free account

Already signed in? Go to the CSP dashboard.

Frequently Asked Questions

What is a CSP report-uri endpoint?
It is a URL you put in your Content-Security-Policy header. Whenever a visitor's browser blocks something the policy does not allow, it POSTs a small JSON report to that URL describing what was blocked, which directive blocked it, and on which page. Without such an endpoint a policy is silent: it breaks things in visitors' browsers and you never hear about it. With one, you can ship a strict policy in Report-Only mode first and read what it would have broken before it breaks anything.
Why not just build the endpoint myself?
You can — it is a POST handler that parses JSON. The part that takes the time is everything after: browsers send one report per blocked resource per page view, so a busy site produces tens of thousands of near-identical reports a day, and a meaningful share of them come from visitors' browser extensions rather than from your site. You end up writing deduplication, aggregation, retention and noise filtering before you can read anything. That is what this does.
Is it free?
Yes, with a free account. The account exists because the reports are yours: each monitor gets its own secret collector URL, and the violations, the generated policy and the weekly digest are visible only to you.
What does it do with the reports?
It folds them into one row per day, per directive, per blocked origin — so "a script from cdn.example.com was blocked" is one line with a count, not nine thousand lines. Each row comes with an explanation of what that violation means and the fixes in order, including the policy fragment to copy. Inline script is flagged as the serious one and is deliberately never offered as a one-click allow, because the usual fix for it, 'unsafe-inline', removes most of what a policy is for.
Can it write the policy for me?
It can synthesize one from what your visitors actually loaded over the last 30 days, optionally combined with a fresh crawl of your site. The result starts from a locked-down skeleton — default-src 'self', object-src 'none', base-uri 'self', frame-ancestors 'self' — and adds only origins that were really observed. Origins seen just once are still included but called out for review, because a single sighting is as likely to be an extension as a dependency.
Should I use report-uri or report-to?
Use both for now. report-uri is deprecated in the specification but is still the directive with the widest browser support, while report-to is the replacement and needs an accompanying Reporting-Endpoints header. Browsers that understand report-to ignore report-uri, so listing both means every visitor's browser can report. The collector accepts the payload format of either.
Why do I see violations I cannot place?
Browser extensions inject scripts and styles into pages, and those injections violate your policy exactly like your own code would. They are reported as chrome-extension:// or moz-extension:// origins, or sometimes as an unhelpful "unknown" when the browser deliberately withholds the URL after a cross-origin redirect. These are the visitor's software, not your site, and the right response is to leave them out of the policy. Each such row is labelled as extension traffic so you are not tempted to widen a directive for it.
Does adding a report endpoint slow my site down?
No. The browser sends reports in the background, after the fact, and does not wait for a response before rendering. The collector always replies 204 No Content and never asks the browser to do anything.