Reporting API: Capturing Browser Security Violations
The Reporting API is a modern web standard that provides a centralized mechanism for browsers to capture and automatically send client-side errors, deprecations, and security violations directly to a backend server. By configuring standard HTTP headers, web applications can instruct user agents to deliver detailed JSON reports concerning Content Security Policy (CSP) breaches, cross-origin policy failures, network errors, and browser crashes. This article explores how the Reporting API functions and demonstrates how JavaScript backends can receive and process these security telemetry events to maintain robust application security.
What is the Reporting API?
The Reporting API standardizes how browsers collect and report client-side anomalies without requiring custom client-side JavaScript error-tracking libraries. Instead of relying on script execution—which might be blocked or fail during critical security incidents—the browser itself queues and dispatches reports asynchronously out-of-band.
The API can capture a wide range of reports, including: * Content Security Policy (CSP) Violations: Unauthorized script executions, inline style injections, or prohibited resource fetching. * Cross-Origin Opener Policy (COOP) and Cross-Origin Embedder Policy (COEP) Violations: Failures in cross-origin isolation and resource sharing. * Permissions Policy Violations: Unauthorized usage of browser features such as the camera, microphone, or geolocation. * Deprecation and Crash Reports: Use of obsolete browser APIs or tab crashes.
Configuring the Reporting Endpoints
To activate the Reporting API, the backend server must send the
Reporting-Endpoints HTTP response header. This header maps
a named endpoint identifier to a specific URL on your JavaScript
backend.
Reporting-Endpoints: security-endpoint="https://api.example.com/reports/security", default="https://api.example.com/reports/general"
Once defined, other security headers reference these named endpoints.
For example, to monitor Content Security Policy violations without
blocking resources (using report-only mode) or in standard enforcement
mode, specify the report-to directive:
Content-Security-Policy: default-src 'self'; script-src 'self'; report-to security-endpoint;
For cross-origin policies:
Cross-Origin-Opener-Policy: same-origin; report-to="security-endpoint"
Cross-Origin-Embedder-Policy: require-corp; report-to="security-endpoint"
How the Browser Transmits Violation Reports
When a security violation occurs, the browser creates a report and
places it in an internal queue. To optimize performance and conserve
battery life, browsers batch multiple reports and send them via an
asynchronous HTTP POST request to the configured
endpoint.
The request uses the application/reports+json
Content-Type and delivers an array of JSON objects structured as
follows:
[
{
"type": "csp-violation",
"age": 42,
"url": "https://example.com/dashboard",
"user_agent": "Mozilla/5.0 ...",
"body": {
"blockedURL": "https://malicious-cdn.com/injected.js",
"disposition": "enforce",
"documentURL": "https://example.com/dashboard",
"effectiveDirective": "script-src-elem",
"originalPolicy": "default-src 'self'; script-src 'self'; report-to security-endpoint;",
"statusCode": 200
}
}
]Handling Reports on a JavaScript Backend
JavaScript backends, such as those built with Node.js and Express,
must handle POST requests with the
application/reports+json MIME type to properly ingest
violation payloads.
import express from 'express';
const app = express();
// Parse standard JSON and application/reports+json payloads
app.use(express.json({ type: ['application/json', 'application/reports+json'] }));
app.post('/reports/security', (req, res) => {
const reports = req.body;
if (Array.isArray(reports)) {
for (const report of reports) {
console.warn(`[Security Violation] Type: ${report.type}`, {
url: report.url,
body: report.body,
receivedAt: new Date().toISOString()
});
// Route reports to logging pipelines, SIEMs, or alert systems
}
}
// Always respond with a 204 or 200 to acknowledge receipt
res.status(204).end();
});
app.listen(3000, () => {
console.log('Reporting server listening on port 3000');
});Benefits for Web Security Operations
- Zero Client Performance Overhead: Because the browser handles logging natively, report collection does not block the main execution thread or consume JavaScript execution time.
- Resilience Against Tampering: Malicious scripts running on a compromised page cannot easily intercept or alter the browser’s native reporting mechanism.
- Actionable Real-Time Telemetry: Backend systems gain immediate visibility into Cross-Site Scripting (XSS) attempts, broken asset dependencies, and strict policy regressions before they impact the wider user base.