Content-Security-Policy-Report-Only security response
Tests a Content Security Policy without enforcing it — violations are reported but resources are still loaded.
What it does
Content-Security-Policy-Report-Only lets you test a Content Security Policy without enforcing it. Resources blocked by the policy still load normally — the browser doesn't block anything. Instead, it sends violation reports to a specified endpoint for every policy violation it would have enforced.
This is the safe way to deploy CSP. Running a new policy in report-only mode first reveals exactly what it would break, without actually breaking it for your users.
Syntax
Content-Security-Policy-Report-Only: <policy>; report-uri <url>
Content-Security-Policy-Report-Only: <policy>; report-to <group-name>
The policy syntax is identical to Content-Security-Policy — every directive works the same way. The only difference is enforcement: in report-only mode, violations are reported but not blocked.
Example:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-abc123'; report-uri /csp-report
Violation reports
When a violation occurs, the browser POSTs a JSON report to your report-uri:
{
"csp-report": {
"document-uri": "https://example.com/page",
"referrer": "",
"violated-directive": "script-src-elem",
"effective-directive": "script-src-elem",
"original-policy": "default-src 'self'; script-src 'self'",
"disposition": "report",
"blocked-uri": "https://cdn.jquery.com/jquery.min.js",
"status-code": 200,
"script-sample": ""
}
}
Key fields:
violated-directive— which directive was violatedblocked-uri— the resource that would have been blockeddisposition—"report"(report-only) or"enforce"(enforcing CSP)document-uri— the page where the violation occurredscript-sample— first 40 characters of inline script that violated the policy
The deployment workflow
The recommended path to a strict CSP:
Step 1 — Shadow mode with a permissive policy:
Content-Security-Policy-Report-Only: default-src 'self' 'unsafe-inline'; report-uri /csp-report
Even a loose policy in report-only mode generates useful violation data.
Step 2 — Tighten the policy and keep collecting:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-{NONCE}'; report-uri /csp-report
Analyse reports. Fix violations (move inline scripts to external files, add nonces, whitelist CDN domains).
Step 3 — Run both headers simultaneously:
Content-Security-Policy: default-src 'self'; script-src 'self'
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-{NONCE}'
The enforcing header protects with your current known-good policy. The report-only header tests the next, stricter version. Violations from the stricter policy are reported without affecting users.
Step 4 — Promote report-only to enforcing:
Once reports are clean, move the stricter policy to Content-Security-Policy.
report-uri vs report-to
report-uri (older, widely supported):
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violations
The browser POSTs JSON directly to this URL. Simple but deprecated in CSP3.
report-to (newer, uses Reporting API):
Reporting-Endpoints: csp-endpoint="https://example.com/csp-report"
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint
More flexible — batches reports, supports other report types (network errors, deprecations). Less universally supported than report-uri.
Practical advice: Use both for maximum coverage — report-uri for older browsers, report-to for modern ones.
Common noise in reports
Not all violation reports represent real problems to fix. Common sources of noise:
Browser extensions. Extensions inject scripts and styles into every page. These violations are real but not your problem — you can't (and shouldn't) whitelist every extension. Filter them out in your reporting endpoint by checking blocked-uri and document-uri.
data: URIs in images. If you haven't set img-src data: and your CSS uses data URIs for small images, you'll get violations. Easy fix.
Injected ads/analytics. Third-party ad networks and analytics inject scripts dynamically. Your policy needs to account for these if you use them.
inline violations from Google Tag Manager. GTM injects inline scripts. The right solution is adding GTM's hash or nonce, not 'unsafe-inline'.
Real-world examples
Initial audit of an existing site:
Content-Security-Policy-Report-Only: default-src *; script-src * 'unsafe-inline' 'unsafe-eval'; report-uri /csp-audit
Ultra-permissive — almost nothing will violate this, but it establishes the reporting pipeline.
Testing a strict policy:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-rAnd0mV4lu3'; style-src 'self' fonts.googleapis.com; img-src 'self' data:; font-src fonts.gstatic.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; report-uri /csp-violations
Running both enforcing and report-only simultaneously:
Content-Security-Policy: default-src 'self'; script-src 'self'
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-abc123' 'strict-dynamic'; report-uri /csp-next
FAQ
Can I use both Content-Security-Policy and Content-Security-Policy-Report-Only at the same time?
Yes, and this is best practice during policy migration. Both headers are processed independently. The enforcing policy blocks violations; the report-only policy monitors a stricter candidate policy. Users see no difference; you see exactly what would break if you enforced the stricter policy.
Does report-only protect against attacks at all?
No. Report-only does not block anything. An XSS attack on a page with only a report-only CSP will execute successfully. The value is purely operational — gathering data to build an enforcing policy. Never use report-only as your only CSP header on sensitive pages.
How should I handle the reporting endpoint?
Keep it simple: accept POST requests, log the JSON body, return 204. Violations can be noisy (especially from browser extensions) — pipe them into a logging service with filtering and alerting rather than raw storage. Services like Sentry, Report URI (reporturi.com), and Datadog have built-in CSP report handling.
Fun fact
The report-uri directive was deprecated in CSP3 in favour of the report-to directive, which uses the broader Reporting API. However, report-to has had spotty browser support for years, creating an awkward situation where the officially deprecated mechanism is the more reliable one. As of 2026, most production CSP deployments still use report-uri as the primary reporting mechanism — a reminder that "deprecated" in web standards means "we prefer you use something else" not "we've removed this."