How the Reporting API works
How the browser queues, batches, and delivers reports out of band to the endpoint you declare.
Last update:
The Reporting API separates the thing that produces a report (a policy like CSP or COOP) from the thing that delivers it (the browser's report queue). A policy flags an event, the browser collects it, batches it with others, and sends it to your endpoint later, independent of the page. That decoupling is why a report can still arrive after the page has navigated away or crashed.
How the Reporting API delivers reports
The producers are the policies and platform features: Content Security Policy, Cross-Origin-Opener-Policy (COOP), Cross-Origin-Embedder-Policy (COEP), Permissions-Policy, Document-Policy, Integrity-Policy, Network Error Logging, and the implicit features that emit deprecation, intervention, and crash reports. Each of these decides when something is worth reporting.
The Reporting API is the shared machinery underneath them. It defines no policy behavior of its own; it only structures each report into a common envelope, queues it, and delivers it. That is the key idea: the same transport carries a CSP violation, a network error, and a deprecation warning, so you configure delivery once and every producer uses it.
Declaring where reports go
You declare endpoints with one response header, Reporting-Endpoints, mapping a
name to an HTTPS URL. A policy then references that name to route its reports.
Reporting-Endpoints: default="https://<Endpoint-ID>.report.centralcsp.com"CSP names an endpoint with its report-to directive; COOP and COEP use a
report-to= parameter; Integrity-Policy uses endpoints=(); and the implicit
report types go to the default reporting endpoint. The full syntax, and the older
Report-To header that NEL still needs, are in the
Headers section.
Queueing, batching, and delivery
When a report is generated, the browser does not send it on its own. It adds the
report to a queue, groups queued reports by endpoint and then by origin, and
delivers each group as a single HTTP POST with
Content-Type: application/reports+json. One delivery can therefore carry several
reports of different types.
Timing is up to the browser, not the spec. Chromium batches and can delay delivery by up to about a minute to save battery and bandwidth on mobile, so a report you trigger now may not arrive for a little while. That is normal; design your endpoint to accept reports whenever they show up rather than expecting them in real time.
Best-effort, not guaranteed
The spec is explicit that delivery is best-effort. Reporting is not a reliable communications channel, and there is no defined retry mechanism, so do not build logic that depends on every report arriving.
Delivery can also stop. The browser tracks failures per endpoint, and an endpoint
that repeatedly fails, or that responds with HTTP 410 Gone, is dropped and stops
receiving reports. So an endpoint that returns errors does not just lose the current
batch; it can be removed entirely. Return a 2xx status from your receiver, and use
410 deliberately if you ever want the browser to stop sending.
See also
- The report delivery format
- Report-To vs Reporting-Endpoints
- Headers
- Collect and aggregate every report type with CentralCSP reporting.