CentralCSP
PoliciesContent-Security-Policy

Report-Only

The Content-Security-Policy-Report-Only header reports what a policy would block without enforcing it, for safe CSP rollout.

Last update:

The Content-Security-Policy-Report-Only header tells the browser to check a Content Security Policy (CSP) and report every violation, without blocking anything. You see exactly what a policy would break before it breaks it. That makes it the safe way to roll out or tighten a policy on a live site.

A candidate policy under test, paired with the endpoint that receives its reports:

Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint

Quick overview

A Report-Only policy is written exactly like an enforced one. The only difference is the header name and the resulting disposition. With Content-Security-Policy-Report-Only the disposition is report, so the browser loads the resource and sends a CSP violation report instead of applying the Content-Security-Policy header's enforce behavior.

Content-Security-Policy-Report-Only:
    default-src 'self';
    script-src 'self' 'nonce-r4nd0m';
    object-src 'none';
    base-uri 'none';
    report-to csp-endpoint

A report-only policy is useful only if it has somewhere to send reports. Pair it with a report-to directive (and the legacy report-uri only if you need to cover old browser versions) so the violations reach an endpoint you control.

Values

The header value is a CSP, the same semicolon-separated list of directives you would put in an enforced policy. Every CSP directive is valid here. The directives that actually matter in Report-Only are the reporting ones, since nothing is enforced.

PartStatusWhat it does
The directive list✅ GoodDefines the policy the browser evaluates against the page.
report-to✅ GoodNames the endpoint group that receives violation reports.
report-uri⚠️ DeprecatedLegacy reporting target, kept only for old, unupdated browser versions.

A response can carry both headers at once. Send an enforced Content-Security-Policy for the policy you trust today, and a separate Content-Security-Policy-Report-Only for the stricter policy you are testing. The browser evaluates each independently and reports the report-only one without touching the page.

Insecure values to avoid

A Report-Only policy never protects anything, so it has no insecure values of its own. The mistake is treating it as protection. Shipping only Content-Security-Policy-Report-Only in production means the browser reports attacks but blocks none of them. An attacker's inline script still runs. Use Report-Only to learn what to enforce, then move the validated policy to the enforcing Content-Security-Policy header.

A second mistake is a report-only policy with no reporting directive at all. With no report-to or report-uri, the browser evaluates the policy and discards every violation, so you learn nothing.

Why this header exists

A real policy almost always breaks something on the first try: an inline script, a third-party widget, a data: image. Enforcing an untested policy takes the site down. Report-Only was introduced so you can deploy a candidate policy, watch the violations roll in from real traffic, fix the gaps, and only then enforce it. It turns CSP rollout from a guess into a measurement.

What it protects against

The header itself blocks nothing, so it protects nothing directly. Its security value is indirect. It lets you reach a strict, enforced Content-Security-Policy safely. The faster you can validate a tight policy, the sooner you get real protection against cross-site scripting (XSS) and injection. Collecting the report-only violations in CentralCSP shows you exactly which directives to adjust before you flip to enforcement.

Known bypasses and limitations

A <meta http-equiv> tag cannot deliver a Report-Only policy. The meta element only supports the enforcing Content-Security-Policy, so Report-Only is an HTTP response header exclusively. The same applies to the reporting directives (report-to, report-uri), which a meta tag also cannot set.

Report-Only violations are reported per policy, so a noisy candidate policy can generate a large volume of reports on a high-traffic page. Sample and triage them rather than reading raw JSON.

Risks of misconfiguration

The dominant risk is confusing the two headers. Leaving a strict policy in Report-Only forever gives a false sense of security with zero enforcement, while promoting an untested policy straight to Content-Security-Policy breaks the page. Validate in Report-Only, then enforce.

Recommendation

Whenever you tighten a policy, run the candidate in Report-Only alongside the enforced one, and point both at report-to with a Reporting-Endpoints declaration.

Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
Content-Security-Policy-Report-Only:
    script-src 'nonce-r4nd0m' 'strict-dynamic';
    object-src 'none';
    base-uri 'none';
    report-to csp-endpoint

Every current engine delivers report-to reports now, so this pair is the primary transport; add report-uri only for old-version tails. Staged rollout through Report-Only is the approach the web.dev strict CSP guide recommends.

How to set it up

  1. Send Content-Security-Policy-Report-Only with your candidate policy and a report-to directive that names an endpoint group.
  2. Declare that group with the Reporting-Endpoints header so the browser knows where to send reports.
Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
Content-Security-Policy-Report-Only:
    default-src 'self';
    script-src 'self' 'nonce-r4nd0m';
    object-src 'none';
    base-uri 'none';
    report-to csp-endpoint
  1. Watch the CSP violation reports, fix the legitimate breakages, and once the policy is clean, move it to the enforcing Content-Security-Policy header. The Reporting API configuration checker confirms your endpoint wiring works before you rely on the reports.

Browser support

Widely supported. Content-Security-Policy-Report-Only works across modern browsers. The report-to plus Reporting-Endpoints transport is now cross-browser too, supported in current Chrome, Safari, and Firefox (Firefox was the last engine to add it). Keep report-uri only to cover old, unupdated browser versions.

See also

Sources

On this page