Collect errors directly from the browser, no custom tracking JS needed
The Reporting API gives browsers a standardized way to automatically report problems like CSP violations, deprecated API usage, or even tab crashes to a central endpoint. Unlike classic error tracking, this happens largely without additional JavaScript and captures cases custom code would never see.
Table of Contents
- 1. The gap in classic error tracking
- 2. The Report-To header as a configuration mechanism
- 3. Capturing CSP violations automatically
- 4. ReportingObserver for client-side evaluation
- 5. Spotting deprecation warnings and interventions early
- 6. Crash and intervention reports
- 7. Privacy and the scope of reports
- 8. Browser support and practical limits
- 9. A complete example setup
- 10. Summary
- 11. FAQ
1. The gap in classic error tracking
Classic frontend error monitoring is usually based on window.onerror, window.addEventListener('unhandledrejection', ...), and explicit try-catch. These mechanisms reliably capture JavaScript runtime errors but are blind to problems that arise at a deeper browser level, such as resources blocked by a Content Security Policy or the use of a browser API marked as deprecated.
This is exactly the gap the Reporting API closes. It defines a channel through which the browser itself, independent of the execution of custom JavaScript, generates structured reports about certain event categories and either sends them to a configured endpoint or makes them available locally via a JavaScript interface.
2. The Report-To header as a configuration mechanism
For the browser to know where reports should be sent, the server defines named destination URLs via the HTTP header Reporting-Endpoints. Earlier implementations used the now superseded Report-To header with a JSON value, modern browsers increasingly follow the simpler Reporting-Endpoints format.
Once an endpoint is named, it can be referenced in other headers, for example in the Content Security Policy via the report-to directive, to send CSP violations there. The browser handles batching and delivery on its own, including retries for failed deliveries, without the page itself needing to stay active.
// Example server response headers (not settable via JS, shown for reference)
// Reporting-Endpoints: default="https://reports.example.com/csp"
// Content-Security-Policy: default-src 'self'; report-to default
// Snippet showing how a server would generate such headers (Node/Express)
app.use((req, res, next) => {
res.setHeader(
'Reporting-Endpoints',
'default="https://reports.example.com/csp"'
);
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; report-to default"
);
next();
});
3. Capturing CSP violations automatically
Without the Reporting API, developers often only notice CSP violations once users complain about broken functionality, since a blocked script or image usually fails silently in the background for the user. With a configured report endpoint, the browser automatically sends a structured report with the blocked URI, the violated directive, and the line in the source code every time a violation occurs.
This makes the Reporting API especially valuable while introducing or tightening a CSP: instead of blindly tightening the policy and waiting for user complaints, it can first be run in report-only mode, while real violations are collected and analyzed in the background before the policy is actually enforced.
4. ReportingObserver for client-side evaluation
Besides server-side collection via headers, the ReportingObserver also allows reacting to reports on the client side without waiting for a server round trip. This is useful for custom debugging during development or to gather additional context before a report is forwarded to a custom analytics system.
The observer is created with a callback function and optional filters for specific report types, and is activated via observe(). Using the buffered: true option, you can even retrieve reports generated before the observer was created, as long as the page is still active.
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
console.log('Report type:', report.type);
console.log('URL:', report.url);
console.log('Body:', report.body);
// Example: forward to an internal dashboard
fetch('/internal/browser-reports', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: report.type,
url: report.url,
body: report.body,
}),
});
}
}, { types: ['deprecation', 'intervention'], buffered: true });
observer.observe();
5. Spotting deprecation warnings and interventions early
Browsers regularly mark APIs as deprecated before removing them, for example old synchronous XHR calls on the main thread or certain outdated DOM methods. These warnings normally only appear in the developer console and are never seen by real users, which means a team often only learns about the usage in production after the feature has actually been removed.
Via the deprecation report type, such warnings can be collected centrally from all users, not just from developers who happen to have the console open. This allows proactively identifying deprecated code paths in your own application or in third-party scripts, long before a browser update actually removes the feature and users are affected. This is especially valuable for third-party scripts whose source code you cannot inspect yourself, but whose deprecation warnings still become visible via your own reporting endpoint as soon as they execute in the context of your page.
6. Crash and intervention reports
A particularly valuable report type is crash, which fires when a tab crashes or is terminated due to memory exhaustion, a state that classic JavaScript error tracking naturally cannot capture, since the tab is no longer executing any code at that point. The report is instead created by the browser process itself and delivered at the next possible opportunity.
Something similar applies to intervention reports, which arise when the browser actively steps in against problematic behavior, for example blocking autoplay audio without user interaction. These signals point out where the application violates browser heuristics for good user experience, often things that simply do not show up in your own testing.
Crash reports in particular provide insights in practice that would otherwise only surface through indirect signals like support tickets or a noticeably high bounce rate on certain pages. A team that systematically evaluates crash reports by affected route, browser version, and memory footprint can spot patterns, for example a specific image gallery component that regularly causes memory exhaustion on older mobile devices, long before enough users actively complain.
7. Privacy and the scope of reports
Since reports can contain information such as URLs, user agent data, and technical details, your own reporting endpoint must be treated like any other data collection under privacy law. It is advisable to restrict the endpoint to your own infrastructure and not forward reports unchecked to third parties.
For CSP reports specifically, an additional concern is that blocked URIs can contain sensitive information like internal system names or query parameters if an attacker tries to exfiltrate data via the blocked resource. Server-side filtering and regular cleanup of collected reports are therefore part of a clean reporting setup.
8. Browser support and practical limits
Chrome and other Chromium-based browsers broadly support the Reporting API including Report-To or Reporting-Endpoints, Firefox and Safari have parts of it, particularly CSP reporting via report-uri or report-to, but not always the full ReportingObserver feature set. A production setup should therefore not rely exclusively on client-side evaluation.
In practice, a combination proves effective: server-side endpoints for all supported report types as the primary data source, supplemented by a ReportingObserver for browsers that support it, to attach additional client-side context like the current user route to the report before it is forwarded. It is also worth regularly reviewing incoming report statistics by browser family, to spot early when a specific browser suddenly reports noticeably more violations or deprecation warnings than before, a strong signal of a recently rolled out browser update with changed behavior.
9. A complete example setup
A complete reporting setup combines server-side header configuration with a client-side observer that adds extra context, such as which route the user currently visits or which feature flags are active. This produces reports that describe not just the technical problem but also the circumstances under which it occurred.
The table below compares the most important report types, their triggers, and whether they are visible client-side via the ReportingObserver or only server-side via the configured endpoint.
function initReporting(currentRoute) {
if (!('ReportingObserver' in window)) return;
const observer = new ReportingObserver((reports) => {
for (const report of reports) {
fetch('/internal/browser-reports', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: report.type,
url: report.url,
body: report.body,
route: currentRoute,
timestamp: Date.now(),
}),
});
}
}, { buffered: true });
observer.observe();
}
initReporting(window.location.pathname);
| Report type | Trigger | Visible via ReportingObserver | Typical use |
|---|---|---|---|
| csp-violation | Resource blocked by a CSP directive | Partially, browser dependent | Test CSP rollout without user complaints |
| deprecation | Use of an API marked as deprecated | Yes | Proactively find deprecated code paths |
| intervention | Browser actively blocks problematic behavior | Yes | Detect violations of UX heuristics |
| crash | Tab crash or memory exhaustion | No, server-side only | Surface stability issues JS cannot capture |
Mironsoft
Modern browser APIs, performance, and maintainable JavaScript
JavaScript that holds up in the real browser, not just in the tutorial?
We review existing frontend code for outdated patterns, unnecessary dependencies, and performance traps, then replace them with modern, native browser APIs that mean less bundle weight and less maintenance burden.
Code Review
Systematically finding outdated patterns, unnecessary dependencies, and memory leaks.
Performance Optimization
Improving bundle size, load time, and runtime performance with modern APIs.
Modernization
Deliberately introducing native browser APIs instead of heavy libraries.
10. Summary
Reporting API: The Essentials at a Glance
Configuration
The Reporting-Endpoints header defines destination URLs, CSP references them via report-to.
Client side
ReportingObserver reads reports directly in the browser, enriched with custom context.
Strength
Captures cases like crashes that classic JS error tracking would never see.
Limit
Support varies by browser, server-side endpoints remain the more reliable base.