# How to read a CSP violation report (/en/blog/csp-violation-report-fields)



You turned on Content Security Policy (CSP) reporting, the reports are arriving, and
now you are staring at JSON trying to work out what blocked what. The field names
are terse, some values are redacted, and the same event can arrive in two different
shapes depending on how you wired reporting. This is a field-by-field guide to a CSP
violation report so you can read one at a glance and know which line of your policy
to change.

There are two formats for the same underlying event. The modern
[`csp-violation` report](/en/docs/web-security/reporting-api/reports/csp-violation) is delivered
through the Reporting API as part of a batch; the legacy `report-uri` format is a
single object the browser POSTs on its own. They carry the same information under
different field names, so once you can read one you can read the other.

## The modern shape, csp-violation [#the-modern-shape-csp-violation]

This is what you get when the policy uses the `report-to` directive. The browser
delivers an array with `Content-Type: application/reports+json`, and the
CSP-specific data sits in `body` with camelCase field names.

```json
{
  "type": "csp-violation",
  "age": 53,
  "url": "https://example.com/",
  "user_agent": "Mozilla/5.0 ...",
  "body": {
    "documentURL": "https://example.com/",
    "referrer": "https://www.google.com/",
    "blockedURL": "https://apis.google.com/js/platform.js",
    "effectiveDirective": "script-src-elem",
    "originalPolicy": "default-src 'self'; report-to csp-endpoint",
    "sourceFile": "https://example.com/",
    "sample": "",
    "disposition": "enforce",
    "statusCode": 200,
    "lineNumber": 1441,
    "columnNumber": 59
  }
}
```

| Field                         | What it means                                                                                             |
| ----------------------------- | --------------------------------------------------------------------------------------------------------- |
| `documentURL`                 | The page where the violation happened.                                                                    |
| `blockedURL`                  | The resource that was blocked. Cross-origin, this is truncated to scheme, host, and port.                 |
| `effectiveDirective`          | The directive that actually fired (for example `script-src-elem`).                                        |
| `originalPolicy`              | The full policy text the browser enforced.                                                                |
| `disposition`                 | `enforce` for a blocking policy, `report` for Report-Only.                                                |
| `sample`                      | First \~40 characters of the offending inline content, only when the directive carries `'report-sample'`. |
| `statusCode`                  | HTTP status of the document response.                                                                     |
| `lineNumber` / `columnNumber` | Location for inline violations.                                                                           |

Two of these surprise people. `blockedURL` is deliberately redacted to the origin
for cross-origin resources, so you see `https://cdn.example/` rather than the exact
path; that is a privacy measure, not a bug. And `effectiveDirective` is the specific
directive that fired, which can be more granular than what you wrote: a blocked
`<script>` tag reports `script-src-elem` even if your policy only set `script-src`,
because `script-src` is the fallback the granular directive inherits from.
[script-src-elem vs script-src-attr](/en/blog/script-src-elem-vs-script-src-attr)
walks through that split and what each granular directive controls.

If you parse these at your endpoint, here is the modern body as a TypeScript type you
can drop into the handler. Hover any field to see its type; `disposition` is a union
of the only two values it ever holds.

```ts twoslash
// The body of a modern csp-violation report.
interface CspViolationBody {
  documentURL: string;
  referrer: string;
  blockedURL: string;
  effectiveDirective: string;
  originalPolicy: string;
  sourceFile: string;
  sample: string;
  disposition: "enforce" | "report";
  statusCode: number;
  lineNumber: number;
  columnNumber: number;
}

interface CspViolationReport {
  type: "csp-violation";
  age: number;
  url: string;
  user_agent: string;
  body: CspViolationBody;
}

function readViolation(report: CspViolationReport) {
  const blocked = report.body.blockedURL;
  const enforced = report.body.disposition === "enforce";
  return { blocked, enforced };
}
```

## The legacy shape, report-uri [#the-legacy-shape-report-uri]

When the policy uses the `report-uri` directive, the browser posts a single object
with `Content-Type: application/csp-report` and kebab-case field names.

```json
{
  "csp-report": {
    "document-uri": "https://example.com/",
    "blocked-uri": "https://apis.google.com/js/platform.js",
    "effective-directive": "script-src-elem",
    "violated-directive": "script-src-elem",
    "original-policy": "default-src 'self'; report-uri /csp-reports",
    "disposition": "enforce",
    "status-code": 200,
    "script-sample": ""
  }
}
```

The mapping is one to one: `blockedURL` is `blocked-uri`, `effectiveDirective` is
`effective-directive`, `sample` is `script-sample`, and so on. The one extra field,
`violated-directive`, is a historic alias for `effective-directive` and can be
ignored. If your endpoint accepts both formats, branch on the `Content-Type` header.
Which directive you should be sending, and why you often send both, is covered in
[report-uri vs report-to](/en/blog/report-uri-vs-report-to).

## Reading a report in practice [#reading-a-report-in-practice]

Read `effectiveDirective` and `blockedURL` together; that pair tells you almost
everything. A `script-src` violation pointing at a host you recognize (a CDN, an
analytics provider) usually means your policy is too strict and you need to allow
that source. A violation pointing at a host you do not recognize, or an inline
`sample` you did not write, is worth investigating as a possible injection. To trace a single violation back to the line that caused it, [debug CSP violations in Chrome DevTools](/en/blog/debug-csp-violations-devtools).

The `sample` field is the most useful for inline violations, but it only appears
when the directive carries the `'report-sample'` keyword, and it is capped at about
40 characters, enough to identify the snippet, not to leak a secret. `disposition`
tells you whether this was a real block (`enforce`) or a dry run (`report`), which
matters when you are running an enforced policy and a Report-Only policy at once.

Treat every field as attacker-influenced input. A report is data the page caused the
browser to send, so never render `blockedURL` or `sample` into an admin page without
escaping.

## Stop reading raw JSON [#stop-reading-raw-json]

Reading one report is fine; reading thousands is not. CentralCSP ingests both shapes,
normalizes them into one model, and groups violations by directive and blocked host,
so instead of a log file you get a ranked list of what is actually breaking and how
often. It [shows them on a dashboard](/en/docs/platform/monitoring/csp) you can act
on, which is the difference between collecting reports and using them.

## Next steps [#next-steps]

* Set reporting up in [Get started with CSP reporting](/en/blog/get-started-csp-reporting).
* See the envelope in [the report delivery format](/en/docs/web-security/reporting-api/concepts/report-delivery-format).
* Tighten the policy that produced these with the [CSP evaluator](/tools/csp-evaluator).

[Collect and group CSP reports automatically](/register).

## Sources [#sources]

* [W3C, CSP Level 3 - violation reports](https://www.w3.org/TR/CSP3/#create-violation-for-global)
* [W3C, Reporting API](https://www.w3.org/TR/reporting-1/)
* [MDN, CSPViolationReportBody](https://developer.mozilla.org/en-US/docs/Web/API/CSPViolationReportBody)
* [MDN, SecurityPolicyViolationEvent](https://developer.mozilla.org/en-US/docs/Web/API/SecurityPolicyViolationEvent)
