Content-Security-Policy-Report-Only: testing a policy safely
Content-Security-Policy-Report-Only evaluates a policy and reports every violation it would have caused, but blocks nothing. It is how you find out what a policy breaks before it breaks it.
At a glance
- Direction
- Response only. It has no meta element equivalent.
- Defined in
- Content Security Policy Level 3 (W3C)
- When absent
- Nothing is measured. You learn what a policy breaks only after you enforce it.
- Blocking behaviour
- None. Every violation is logged and the resource still loads.
- Needs a reporting sink
- report-to with Reporting-Endpoints, or the older report-uri directive
- Runs alongside
- A separate enforcing Content-Security-Policy header, evaluated independently
- In the headers checker
- Shown in the raw response header table. Only the enforcing Content-Security-Policy header is graded.
In detail
The report-only header takes exactly the same policy syntax as the enforcing one. The browser evaluates every fetch against it, records anything that would have been blocked, and then loads the resource anyway. The console still shows the violation, marked to make clear the policy was not enforced, so a developer reading the console sees the future failure without the site breaking under them.
It is only useful with somewhere to send the results, because most violations happen on other people's machines with extensions and injected scripts you will never see locally. Add a report-to directive naming an endpoint group declared in Reporting-Endpoints, or the older report-uri directive that posts a JSON document straight to a URL you control. Sending both is normal while browser support catches up.
The intended workflow is to write the policy you want, ship it report-only, watch the reports for a full traffic cycle including a weekday peak and a weekend, fix the genuine gaps, then move the identical string to the enforcing header. Skipping the measurement step is how a marketing tag or a payment iframe disappears silently in production.
You can keep both headers live permanently. A common arrangement is a proven enforcing policy plus a stricter candidate running report-only next to it, which gives a continuous signal about whether the tighter version is ready. The two are evaluated separately and the report-only one never affects what loads.
Examples
Content-Security-Policy: default-src 'self' https://cdn.example.com
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"{
"csp-report": {
"document-uri": "https://example.com/checkout",
"violated-directive": "script-src 'self'",
"blocked-uri": "https://analytics.example.net/tag.js"
}
}This is the shape posted by the older report-uri directive. The Reporting API wraps the same information in its own envelope.
Things that catch people out
- A report-only header with no report-to or report-uri directive produces console noise and nothing else. Nobody is collecting the evidence you set it up to gather.
- Browser extensions inject scripts into pages and generate violation reports that have nothing to do with your code. Expect to filter those out before drawing conclusions.
- Reports are posted by the browser and are unauthenticated, so the endpoint needs rate limiting. A busy site with a broken policy can generate a very large number of them.
Try it: HTTP Headers Checker
Check a live URL or paste headers you already have. It grades six security headers from A+ to F and lists every other header the response returned.
HTTP Headers Checker
LOCAL · Browser-OnlyView HTTP response headers for any URL
Enter any website URL to fetch and analyze its HTTP response headers. We'll automatically add https:// if needed.
Why Check Security Headers?
Security headers protect your website against common attacks like XSS, clickjacking, and MIME sniffing. They're set by your web server and cost nothing to implement. Missing security headers are one of the most common findings in security audits.
Related HTTP headers
All http headers →Content-Security-Policy: directives, sources and reporting
How CSP directives are matched, what default-src actually falls back to, why unsafe-inline defeats the point of a policy, and how nonces and hashes work.
Reporting-Endpoints: where browser reports are sent
How Reporting-Endpoints names collectors for CSP violations and deprecation warnings, how it differs from the older Report-To header, and what arrives.
X-Frame-Options: DENY, SAMEORIGIN and clickjacking
What DENY and SAMEORIGIN do, why ALLOW-FROM never worked, and how CSP frame-ancestors supersedes this header while it is still worth sending.
Permissions-Policy: turning browser features off by default
How Permissions-Policy allowlists are written, the empty parentheses that disable a feature, and how the syntax differs from the Feature-Policy header.
X-Content-Type-Options: nosniff and MIME sniffing
What nosniff stops a browser doing, why it is the safest security header to add, and the two kinds of resource it starts rejecting once you turn it on.
Referrer-Policy: controlling what the Referer header leaks
Every Referrer-Policy value compared, what modern browsers do when the header is missing, and why URLs with tokens in them leak to third parties without it.
Tools for this
- HTTP Headers Checker Free online HTTP headers checker - view HTTP response headers for any URL
- HTTP Request Builder Free online HTTP request builder - build and test HTTP requests with custom headers and body