Express.js middleware for the Reporting API. Automatically wires up report-to / report-uri on your existing policy headers and gives you a ready-made endpoint to collect violation, deprecation, crash, and network error reports.
Plus Deprecation, Intervention, and Crash reports.
Backwards-compatible with CSP Level 2 report-uri for browsers that don't yet support the Reporting API.
npm install reporting-apiPeer dependencies: express, zod, debug.
import express from 'express';
import { reportingEndpoint, setupReportingHeaders } from 'reporting-api';
const app = express();
// 1. Mount the reporting endpoint
app.use('/reporting-endpoint', reportingEndpoint({
allowedOrigins: '*',
onReport(report) {
console.log(report.type, report.body);
},
}));
// 2. Set your policy headers, then let the middleware attach reporters
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', "script-src 'self'");
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
next();
});
app.use(setupReportingHeaders('/reporting-endpoint'));
app.listen(8080);Note
Policy headers must be set before setupReportingHeaders runs so the middleware can append report-to and report-uri directives to them.
The resulting response headers will look like this:
Reporting-Endpoints: reporter="/reporting-endpoint"
Content-Security-Policy: script-src 'self';report-uri /reporting-endpoint?disposition=enforce;report-to reporter
Cross-Origin-Opener-Policy: same-origin;report-to="reporter"
Cross-Origin-Embedder-Policy: require-corp;report-to="reporter"
Returns Express middleware that accepts incoming reports.
| Option | Type | Description |
|---|---|---|
onReport |
(report, req) => void |
Called for every valid report. |
onValidationError |
(error, body, req) => void |
Called when a report fails Zod validation. |
allowedOrigins |
string | RegExp | Array |
Enable CORS for cross-origin reports. Use '*' to allow any origin. |
ignoreBrowserExtensions |
boolean |
Drop CSP violations originating from browser extensions. |
ignoredDeprecationIds |
string[] |
Deprecation report IDs to ignore (e.g. ['AttributionReporting', 'Topics']). |
maxAge |
number |
Maximum report age in seconds. Older buffered reports are dropped. |
debug |
boolean |
Enable debug logging for the reporting-api:* namespace. |
Returns Express middleware that appends report-to / report-uri to every policy header already set on the response and adds the Reporting-Endpoints header.
| Option | Type | Default | Description |
|---|---|---|---|
reportingGroup |
string |
"reporter" |
Reporting group name. |
enableDefaultReporters |
boolean |
false |
Use the default group so you also receive Deprecation, Crash, and Intervention reports. |
enableNetworkErrorLogging |
boolean | object |
false |
Add Report-To + NEL headers (Reporting API v0, required for NEL). Accepts { success_fraction, failure_fraction, include_subdomains }. |
version |
string | number |
— | Appended as a ?version= query param so you can correlate reports with policy revisions. |
Every report delivered to onReport is validated with Zod and has the shape:
{
type: 'csp-violation' | 'coop' | 'coep' | 'deprecation' | 'crash'
| 'intervention' | 'network-error' | 'permissions-policy-violation'
| 'potential-permissions-policy-violation';
body: { /* type-specific fields */ };
url: string;
age: number;
user_agent: string;
report_format: 'report-uri' | 'report-to' | 'report-to-safari';
version?: string;
}Full type definitions are exported as Report and the individual body types (ContentSecurityPolicyReport, CrossOriginOpenerPolicyReport, etc.).
Reports can also be observed in the browser via ReportingObserver:
if (typeof ReportingObserver !== 'undefined') {
new ReportingObserver((reports) => {
reports.forEach(r => console.log(r.body));
}).observe();
}- Reporting API v1 spec (Reporting-Endpoints)
- Reporting API v0 spec (Report-To)
- Migrating from v0 to v1
- v0 vs v1 differences (Chromium)
- Permissions-Policy reporting
Permissions-Policyreports to thedefaultgroup whenreport-tois not set.- COOP and COEP require
report-tovalues wrapped in double quotes (e.g.report-to="group"). - Safari sends reports as
{ body: { ... } }instead of an array and omitsage.