What does the Content-Security-Policy-Report-Only response header do to a resource that the policy it carries would forbid?
answer
- monitors, never enforces
- nothing is ever refused
- same grammar, different instruction
- disposition reads report
- response header field only, no meta
basics
~20 sContent-Security-Policy-Report-Only enforces nothing. A conforming browser loads and runs the resource the policy forbids, records the violation, and sends a report to the destination the policy names. It measures a policy; it does not apply one.
solid answer
~40 s`Content-Security-Policy-Report-Only` carries exactly the same policy grammar as `Content-Security-Policy`, but a conforming browser only **monitors** against it. Every fetch and every inline execution is still checked, and a mismatch still produces a violation record, but the resource is loaded and the inline code still runs. The record carries `disposition` of `report` rather than `enforce`, which is how a collector separates a would-have-broken event from one that actually broke for a user. Two consequences follow. The header buys no protection at all: a page that ships only this one is exactly as exposed as a page with no policy. And it is a response header field only. It is not accepted in a `<meta http-equiv>` element, so a policy that can only be delivered through markup cannot be trialled this way.
code
http · 4 linesHTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Reporting-Endpoints: csp-collector="https://pharmacy.example/csp-reports"
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-to csp-collectorgo deeper
Remember the one-line version: the Report-Only header never refuses anything. It is the header you send when you want to find out what a policy would break, before you are willing to break it.
Be able to explain that the browser runs the full check and produces a full violation record, and that the only difference is the disposition, which reads report instead of enforce. Mention that the Report-Only variant has no markup delivery form.
Show that you treat the header as measurement, not mitigation: a report record describes a hypothetical, so it must not be counted as a blocked attack, and the reports only describe the clients that actually loaded the page.
The trade-off worth owning is that every violation is an outbound request from a page that may be handling sensitive data, so enabling reporting estate-wide is a data-flow decision as much as a security one.
## A header that measures instead of acting `Content-Security-Policy-Report-Only` is a response header field that carries the same policy grammar as `Content-Security-Policy`: the same semicolon-separated directives, the same whitespace-separated source expressions, the same source lists. What differs is what a conforming browser is asked to do with the result. The browser parses the policy, checks every resource fetch and every inline execution against it, and when something does not match it **records a violation and carries on**. The script from the disallowed host is fetched and executed. The inline script element the policy would refuse still runs. The page behaves exactly as it would if the header had never been sent. That is the point of the header. It is an instrument for finding out what a policy *would* do before anyone has to live with what it *does*. ## What "enforces nothing" means concretely For a prescription-status lookup page that sends `Content-Security-Policy-Report-Only: default-src 'self'` and nothing else: - A script pulled from a third-party host is fetched, parsed and executed normally. - An inline `<script>` element in the page still runs. - A connection to an external endpoint still opens and still returns data. - No user ever sees a difference, and nothing on the page breaks because of this header. - Deleting the header changes nothing about how the page behaves; it only changes what you know about it. The practical corollary is the one candidates most often get wrong: **shipping a Report-Only header is not a security measure.** It produces knowledge, not protection. Only a `Content-Security-Policy` header changes what the browser is willing to load or run. ## The disposition field is how a collector tells them apart Every violation record carries a `disposition`, and it has exactly two values: `enforce` and `report`. A policy delivered in `Content-Security-Policy` produces records with `disposition` of `enforce` (something really was refused), and a policy delivered in `Content-Security-Policy-Report-Only` produces records with `disposition` of `report` (something would have been refused). A collector receiving both needs that field to know whether a record describes a user-visible breakage or a hypothetical one. | | Content-Security-Policy | Content-Security-Policy-Report-Only | |---|---|---| | Resource that violates it | refused | loaded and executed | | `disposition` in the record | `enforce` | `report` | | Delivered in a `<meta http-equiv>` element | yes | no, not accepted | | Protection value | the actual defence | none | ## Where the record goes A violation that nobody collects is a violation nobody learns from. Two things can carry the record off the page: 1. The policy names a reporting destination, using `report-uri` (deprecated in CSP Level 3, naming a URL directly) or `report-to` (naming an endpoint declared by a `Reporting-Endpoints` response header field on the same response). The browser then delivers the report to that destination out of band. 2. The page listens for the `securitypolicyviolation` event fired at the document, which carries the same information as a `SecurityPolicyViolationEvent` and lets the page do whatever it likes with it. A Report-Only header with neither a reporting destination nor an in-page listener still monitors, and still surfaces the violation locally to whoever is looking at that one page, but it delivers nothing you can aggregate across an estate. ## The limits worth stating out loud - **It is not a partial defence.** There is no "blocks the worst of it" middle ground; the disposition is binary. - **It is header-only.** The Report-Only variant has no markup delivery form, so a document that can only carry a policy in markup cannot be trialled in report mode. - **It is not free of side effects.** Every violation is a request leaving the browser to your collector, on a page that may be handling identifying data, which is why the specification is careful about what it allows into the report body. - **It tells you about the browsers that ran it.** The report set describes the clients that actually loaded the page, not the policy's behaviour in the abstract. Understood that way, the header answers a single narrow question honestly: *given this exact policy text, what in this page would have stopped working?*
- If a Report-Only policy names no reporting destination at all, is the header useless?Not entirely. Nothing is delivered anywhere, but the browser still evaluates the policy, still surfaces the violation locally, and still fires the `securitypolicyviolation` event at the document, so a page can gather its own records in script. Without `report-uri` or `report-to`, though, there is nothing you can aggregate across users or releases, which is normally the only reason to ship the header.
- What value does the disposition field carry in a report produced by this header, and why does it matter?`report`, as opposed to `enforce` for a record produced by a `Content-Security-Policy` header. A collector needs it to tell a hypothetical violation from a real one: an `enforce` record means a user just had something refused, while a `report` record means only that the policy under trial would have refused it. Treating the two alike overstates or understates real breakage.
A speed camera during its commissioning week: it photographs every car over the limit and files the picture, but no notice is ever posted. The road is exactly as fast as it was before.
saying these in an interview costs you the question
- Thinks Report-Only blocks the resource and additionally reports it
- Believes a Report-Only policy can be delivered in a meta element
- Says shipping Report-Only measurably hardens the page
- Assumes the report's disposition field reads report-only
- Thinks violations are recorded without any destination being needed to aggregate them