All posts

report-uri vs report-to, and how to migrate

CentralCSP Team ·

Last update:

If you have a Content Security Policy (CSP), you have probably seen both report-uri and report-to in examples and wondered which one is correct. The short answer: report-to is the modern directive and report-uri is deprecated. The Reporting-Endpoints header is already cross-browser, and the CSP report-to directive recently became cross-browser too, so you can use report-to for modern browsers and keep report-uri only as a fallback for very old ones. This post explains the difference, why both exist, and gives you a policy you can copy.

The one-line difference

report-uri points a CSP straight at a URL. report-to points a CSP at a named endpoint that is declared separately, in the Reporting-Endpoints header, which is part of the Reporting API. Same goal, getting violation reports to your server, but different plumbing: one is self-contained, the other splits the destination out into a reusable, named endpoint.

Content-Security-Policy: default-src 'self'; report-uri https://<Endpoint-ID>.report.centralcsp.com

The indirection in the second form is deliberate. Because the endpoint is named and declared once, every policy on the page (CSP, COOP, COEP, and the rest) can point at the same endpoint, and the browser can manage delivery for all of them through one mechanism.

report-urireport-to
Status❌ Deprecated✅ Current
DestinationA URL, inline in the policyA name declared in Reporting-Endpoints
Content type postedapplication/csp-reportapplication/reports+json
Payload shapeOne report wrapped in csp-reportA batch of reports, each with type and body
Batches multiple reports❌ No, one POST per violation✅ Yes
Shared with COOP, COEP, NEL, deprecation❌ No, CSP only✅ Yes, one endpoint for all
Browser supportEverywhere, including old browsersChrome, Edge, Firefox, Safari
Keep it becauseIt is the fallback for very old browsersIt is where everything is going

Send both. Browsers that understand report-to ignore report-uri, so the pair costs you nothing and covers every browser.

Why report-uri is deprecated

report-uri came from the era when CSP was the only thing that reported. It posts a single JSON object wrapped in a csp-report key, with Content-Type: application/csp-report. That worked, but every new policy that wanted reporting would have needed its own bespoke directive and its own payload shape.

The Reporting API replaced that with one model for the whole platform: one header to declare endpoints, one delivery format for every report type, and a per-policy way to name an endpoint. That is why the report-to directive plus Reporting-Endpoints superseded the standalone directive. The payloads even differ in casing: the legacy form uses kebab-case (blocked-uri, effective-directive), and the modern csp-violation report uses camelCase (blockedURL, effectiveDirective). An endpoint that accepts both must branch on the Content-Type.

Why report-uri is now just a fallback

For a long time the practical advice was to send both, because the CSP report-to directive delivery only worked in Chromium. That changed: the Reporting-Endpoints header has been cross-browser for a while, and the CSP report-to directive recently became cross-browser too, so it now works across current browsers, including Chrome, Edge, Firefox, and Safari. So report-to alone covers modern traffic.

The two still coexist safely. Browsers that understand report-to ignore report-uri when both are present, so they never double-report. That makes report-uri a clean fallback: keep it only if you need to cover very old, unupdated browsers that predate this cross-browser support.

Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
Content-Security-Policy:
    default-src 'self';
    report-uri https://<Endpoint-ID>.report.centralcsp.com;
    report-to csp-endpoint

If you do not need to support those old browsers, you can drop report-uri and send report-to on its own. Keeping both costs nothing and loses no reports.

Do not confuse the two report-to meanings

The phrase "report-to" is overloaded, which is most of the confusion. There is the CSP report-to directive (used above, inside the policy) and a Report-To HTTP header (the deprecated first-generation reporting header, now replaced by Reporting-Endpoints). They are different things at different layers. The header comparison is covered in Report-To vs Reporting-Endpoints; this post is about the CSP directive.

Looking for a report-uri alternative

If you are searching for a report-uri replacement, you are usually not looking for a different directive, you are looking for where browser reports go and how to receive them without building and running a collector. That is the real cost: receiving the POSTs is easy, but storing, de-duplicating, grouping, and acting on them is a service in its own right.

CentralCSP is that hosted endpoint. It accepts both the legacy application/csp-report object and the modern application/reports+json array, normalizes them into one model, groups violations by directive and blocked host, and turns them into alerts and evidence. Point both directives at it and you are done.

The three delivery methods as tabs, with Reporting-Endpoints selected

Next steps

Sending both directives means your collector has to accept two payload shapes, the single csp-report object and the batched reports+json array, and treat them as the same violation rather than as two. A CentralCSP endpoint parses both formats and normalises them into one report stream per site, so keeping the fallback around does not split your data in half.

Start collecting CSP reports.

Sources