Safeguard
Security Guides•

Roll out a Content Security Policy with report-only mode

How to introduce a Content Security Policy without breaking your site: ship it report-only, read the violation reports, tighten, then enforce.

Safeguard Security Team
3 min read

Roll out a Content Security Policy by sending it first in the Content-Security-Policy-Report-Only header, collecting violation reports for a few weeks, fixing or allowing what is legitimate, and only then switching to the enforcing header. Reports show what would have been blocked, so nothing breaks while you learn.

Why report-only first

A policy that blocks an inline script your checkout depends on will break checkout. Report-only mode avoids that. The browser does not enforce the policy, but sends a report for each violation. MDN notes you can also use it to test a future revision of a live policy.

Both headers can be present in one response. The enforced policy blocks, and the report-only policy reports. That lets you tighten gradually: keep today's enforced policy and trial the stricter one beside it.

One limit to remember: a report-only policy cannot be delivered in a meta element. It has to be an HTTP header.

Step 1: Choose a starting policy

Start from the strict shape MDN recommends rather than a long allowlist:

Content-Security-Policy-Report-Only: script-src 'nonce-RANDOM'; object-src 'none'; base-uri 'none'; report-to csp-endpoint
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"

The nonce must be new and unpredictable on every response, so pages cannot be served as static files with a fixed nonce. Your server needs to template it in.

Step 2: Stand up a report endpoint

MDN recommends the Reporting API with report-to. The older report-uri is deprecated, but MDN says to declare both until report-to is supported everywhere. Reports arrive as JSON by POST with the type csp-violation. Useful fields include the blocked URL, the effective directive, the document URL, the source file and line, and a short sample of the offending code.

Your endpoint should accept the reports content type and store them. Expect noise from browser extensions injecting scripts into pages. Group reports by directive and blocked URL, not by raw count.

Step 3: Triage what you see

Sort violations into three piles:

  • Your own inline code. Move it to files or add nonces through your templates.
  • Third-party scripts you chose to load. Confirm each is still needed, then handle it with nonces plus strict-dynamic, or an allowlist entry.
  • Unknown. Treat as suspicious until someone explains it.

The unknown pile is where the policy earns its keep. A script nobody recognises is worth a ticket.

Step 4: Enforce, then keep watching

When reports are quiet for a full business cycle, including month end or sale periods, copy the policy into the enforcing header. Keep a report-only copy running for your next experiment. Keep the report endpoint live afterwards, since a new violation after launch often means a new script or an injection attempt.

MDN adds a reminder: a CSP is no substitute for sanitising input. Treat it as a second layer.

Quick check

Can I run report-only and enforcing policies together?

Yes. Both are honoured. The enforcing header blocks, and the report-only header only generates reports.

How long should report-only run?

Long enough to cover your normal traffic patterns, including rare pages and seasonal flows. There is no fixed number in the sources, so pick a window and write it down.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.