# Get started with CSP reporting (/en/blog/get-started-csp-reporting)





A Content Security Policy (CSP) is only as good as what you learn from it. Reporting
is how the browser tells you which resources your policy would block, on real pages,
in real browsers you do not own. This guide sets up reporting the modern way, clears
up the confusing tangle of `report-uri`, `report-to`, and the reporting headers, and
shows you what a violation report actually looks like.

The short version: send the [`Reporting-Endpoints`](/en/docs/web-security/reporting-api/headers/reporting-endpoints)
header, point the [`report-to`](/en/docs/web-security/policies/content-security-policy/directives/report-to)
directive at it, and start in [report-only](/en/docs/web-security/policies/content-security-policy/report-only)
so nothing breaks while you learn.

## Why turn on reporting [#why-turn-on-reporting]

You cannot reproduce every browser, extension, and device your visitors use. A policy
that looks clean in your own browser can still block a legitimate script for someone
on a different setup. Reporting is your early warning system: the browser sends a
small JSON report every time the policy blocks something, so you see real violations
before they become support tickets.

## Two directives, two headers (the part everyone confuses) [#two-directives-two-headers-the-part-everyone-confuses]

There are two CSP **directives** (they go inside the policy) and two reporting
**headers** (separate response headers that name an endpoint). The directives say
"report here"; the headers define what "here" means.

Directives, inside `Content-Security-Policy` or `Content-Security-Policy-Report-Only`:

* [`report-uri <url>`](/en/docs/web-security/policies/content-security-policy/directives/report-uri):
  takes a URL directly. **Deprecated**, but still useful as a fallback.
* [`report-to <name>`](/en/docs/web-security/policies/content-security-policy/directives/report-to):
  takes a single name, not a URL. The name is defined by a reporting header. This is
  the current directive.

Reporting headers, which define the named endpoint that `report-to` points at:

* [`Reporting-Endpoints`](/en/docs/web-security/reporting-api/headers/reporting-endpoints):
  the current standard (Reporting API v1). Syntax is `name="https://..."`.
* [`Report-To`](/en/docs/web-security/policies/content-security-policy/directives/report-to):
  the **deprecated** v0 header. You do not need it for a new setup.

This is where the old advice goes wrong. The `Report-To` *header* is the deprecated
thing; the `report-to` *directive* is current. They are not the same. For anything
new, define your endpoint with `Reporting-Endpoints` and reference it from the
`report-to` directive. For the header-level history, see [Report-To vs Reporting-Endpoints](/en/blog/report-to-vs-reporting-endpoints); for the two directives, see [report-uri vs report-to](/en/blog/report-uri-vs-report-to).

## The modern setup, in report-only [#the-modern-setup-in-report-only]

Send two response headers. The endpoint name (`csp-endpoint` here) is yours to choose;
it just has to match in both places. Point it at your CentralCSP endpoint and keep the
policy in report-only at first, so the browser reports violations without blocking
anything:

```http
Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
```

```http
Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self';
  style-src 'self';
  img-src 'self';
  object-src 'none';
  base-uri 'none';
  report-to csp-endpoint
```

The endpoint must be served over HTTPS. The Reporting API ignores non-secure
endpoints, and the endpoint does not need to be on your own origin, so pointing it at
`<Endpoint-ID>.report.centralcsp.com` is exactly the intended pattern.

### Keep report-uri as a fallback (optional) [#keep-report-uri-as-a-fallback-optional]

Browsers that support `report-to` ignore `report-uri`, so adding both does not produce
duplicate reports. The only thing `report-uri` buys you is coverage for older clients
that do not support the newer directive. If you want that belt-and-suspenders fallback,
list both in the same policy:

```http
Content-Security-Policy-Report-Only:
  default-src 'self';
  report-uri https://<Endpoint-ID>.report.centralcsp.com;
  report-to csp-endpoint
```

Note that `report-uri` only works in a real response header, never in a `<meta>` tag.

## What a violation report looks like [#what-a-violation-report-looks-like]

There are two payload shapes, and they use different field names. Do not mix them up. For a field-by-field walkthrough, see [the CSP violation report fields reference](/en/blog/csp-violation-report-fields).

The legacy `report-uri` shape is a single object wrapping `csp-report`, with hyphenated
field names, posted as `application/csp-report`:

```json
{
  "csp-report": {
    "document-uri": "https://example.com/signup",
    "violated-directive": "script-src-elem",
    "effective-directive": "script-src-elem",
    "blocked-uri": "https://apis.google.com/js/platform.js",
    "disposition": "report",
    "status-code": 200,
    "script-sample": ""
  }
}
```

The Reporting API shape (`report-to`) is a JSON array of reports, posted as
`application/reports+json`, with camelCase body fields:

```json
[
  {
    "type": "csp-violation",
    "age": 53531,
    "url": "https://example.com/signup",
    "user_agent": "Mozilla/5.0 ...",
    "body": {
      "documentURL": "https://example.com/signup",
      "blockedURL": "https://apis.google.com/js/platform.js",
      "effectiveDirective": "script-src-elem",
      "originalPolicy": "default-src 'self'; report-to csp-endpoint",
      "disposition": "report",
      "statusCode": 200,
      "sample": ""
    }
  }
]
```

The `disposition` is `report` while you are in report-only and `enforce` once the
policy is enforced. The `sample` field (a short snippet of the offending code) only
appears when you add the `'report-sample'` keyword to the directive. CentralCSP ingests
both shapes, so you do not have to normalize them yourself.

## See and act on your reports [#see-and-act-on-your-reports]

The endpoint is the part that does the work, so it helps to understand [where browser reports go and how to receive them](/en/blog/where-to-send-csp-reports).
Pointing the endpoint at CentralCSP gives you the reports in one place: which origins
are blocked, how often, and on which pages, so you can tell a real problem from a
harmless one and tighten the policy with confidence. From there you move from
report-only to enforced once the noise is gone. The [CSP suite](/platform/csp-builder) handles
the collection and analysis, and the free [CSP scanner](/tools/csp-scanner) checks what
you have shipped.

<img alt="The CSP violations page with reports grouped by directive and blocked origin" src="__img0" width="1365" height="691" />

## Next steps [#next-steps]

* New to policies entirely? Start with [get started with Content Security Policy](/en/blog/get-started-with-csp).
* Building a full policy? See [how to build a strong CSP](/en/blog/how-to-build-a-strong-csp).
* Ready to collect reports? [Create a free account](/register) and get your endpoint.

## Related [#related]

* [The Reporting-Endpoints header explained](/en/blog/reporting-endpoints-header)
* [CSP report-sha keywords explained](/en/blog/csp-report-sha-keywords)

## Sources [#sources]

* [W3C, CSP Level 3 - report-to directive](https://www.w3.org/TR/CSP3/#directive-report-to)
* [W3C, Reporting API](https://www.w3.org/TR/reporting-1/)
* [MDN, Reporting-Endpoints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Reporting-Endpoints)
* [MDN, Content-Security-Policy-Report-Only](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy-Report-Only)
