← Blog

Beyond CSP: the other reports browsers send, and how to sample them

Browsers report broken security policies, deprecated APIs, failed requests and crashes through the same API as CSP. What each type tells you, and how to keep the noisy ones affordable.

Michael Cameron5 min readUpdated

A Content Security Policy report is one type in a larger system. The Reporting API that carries csp-violation reports also carries reports about the other security policies a page sets, about browser features that are going away, and about requests that failed before your server ever answered. HeaderHawk now collects all of them on every plan, the free one included, through the same endpoint your CSP already reports to.

Three kinds of report

Security policies are the closest relatives of CSP. Integrity Policy reports a script loaded without an integrity attribute. Cross-Origin Embedder and Opener Policy report the resources and windows that would break under cross-origin isolation. Permissions Policy reports a page or frame reaching for a feature you denied, such as the camera, and Document Policy reports a page using a feature your own policy turns off. Each has a report-only form, which makes them what a CSP rollout already is: set the policy, read what it would have broken, then enforce. In HeaderHawk they land in the same Issues list and Reports Explorer as your CSP violations, grouped the same way, and a new issue alerts through the same rules.

Browser changes are deprecation and intervention reports. A deprecation report names an API your page called that the browser plans to remove, often with the date it expects to remove it. An intervention report says the browser refused or changed something on the page’s behalf, such as blocking an autoplaying video. These are the reports that tell you, months ahead, that a page will break in a future browser release. They get a page of their own with the soonest removal first, and on Mondays the digest lists every deprecated feature your pages still use that is due to go within 60 days.

Reliability is Network Error Logging and crash reports. NEL reports a request to your origin that failed before a response arrived, with the phase it failed in: DNS, TCP, TLS or the protocol itself. Those are the failures your server logs cannot show, because the request never reached the server. Crash reports say a tab crashed, ran out of memory or stopped responding. Of the major browsers, only Chrome and Edge send either, and both are on a Reliability page by error type, phase and origin, with an alert when a site’s network errors or crashes pass 20 in 15 minutes and five times their usual rate.

Asking for them

A browser sends a report type only when a response header asks for it, and each type has its own header. Most name the endpoint through Reporting-Endpoints, the way report-to does in a CSP:

Reporting-Endpoints: headerhawk="https://your-ingest-code.ingest.us.headerhawk.com/r"
Integrity-Policy-Report-Only: blocked-destinations=(script), endpoints=(headerhawk)
Cross-Origin-Opener-Policy-Report-Only: same-origin; report-to="headerhawk"

Two types break the pattern. Deprecation, intervention and crash reports have no header of their own: Chrome sends them to the endpoint named default, so asking for them means naming your endpoint default. (Chrome sends a crash to an endpoint named crash-reporting instead, if the page declares one.) And NEL predates Reporting-Endpoints, so it still names its endpoint through the older Report-To header, and the browser keeps the policy for as long as its max_age says, whether or not you still send it.

None of this needs typing by hand. A site’s setup in HeaderHawk is a checklist of the report types with a switch for each, and the header block beside it asks for exactly the types switched on. Switch one off and its header leaves the block, so the browser stops sending reports nobody reads. Document Policy is the one type it writes no line for. Which features to turn off is your site’s decision, so your own Document-Policy names the endpoint instead.

Why each type has its own sample rate

Report types differ wildly in volume. A CSP violation fires when something is wrong with your policy. A deprecation report fires every time a page calls the deprecated API, which for a library loaded on every page is every page load. Collected at full rate, one old analytics snippet calling a deprecated API can outnumber every other report your site sends.

So each type has its own sample rate as well as its own switch. Deprecations and interventions start at 5%, script hashes at 10%, and everything else at 100%. At 5% HeaderHawk keeps about one report in twenty and discards the rest before they count towards your quota, as it does every report of a type you have switched off. The kept reports carry the rate they were kept at, and counts are scaled back up by it, so a deprecation page still shows roughly how often each API is called, marked as an estimate. Raise the rate for a type when you want every report, and lower it when a type is spending more of your quota than it earns.

NEL is the exception, and the cheapest of all. Its header carries a failure_fraction, and the browser applies it before sending, so the site’s rate is written into the header and a report the browser never sends costs nothing.

Where to start

Add the security policy headers first. Their headers are report-only, their reports sit in the list you already triage, and each one is a policy you were going to enforce eventually. COEP is the exception, and starts off. It protects nothing on its own: it is half of cross-origin isolation, which only a site using SharedArrayBuffer or WebAssembly threads needs. Its report-only header still reports every cross-origin image or font that has not opted in, a README badge or an avatar, on every page view for as long as it stays. Then leave browser changes at 5% for a week and read what comes back: the deprecations list is short on most sites, and the entries on it are the ones worth fixing before a browser release fixes them for you.

The docs list every report type and the header that asks for it.

Read your own reports this way

Point your policy's reporting at HeaderHawk and see violations grouped by directive, blocked source and page. The Free plan covers 1 site.