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.comThe 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-uri | report-to | |
|---|---|---|
| Status | ❌ Deprecated | ✅ Current |
| Destination | A URL, inline in the policy | A name declared in Reporting-Endpoints |
| Content type posted | application/csp-report | application/reports+json |
| Payload shape | One report wrapped in csp-report | A 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 support | Everywhere, including old browsers | Chrome, Edge, Firefox, Safari |
| Keep it because | It is the fallback for very old browsers | It 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-endpointIf 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.

Next steps
- Set reporting up end to end in Get started with CSP reporting.
- Read the field meanings in the CSP violation report.
- Score your policy with the CSP evaluator.
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.