Safeguard
Developer Tools•

Content Security Policy basics: a safe way to roll one out

A Content Security Policy limits which scripts a page can run and blunts XSS. How to start in report-only mode and move to a nonce-based strict policy.

Safeguard Security Team
3 min read

A Content Security Policy (CSP) is a response header that tells the browser which resources a page may load and run. It cannot replace input sanitizing, but it can block injected script if sanitizing fails. Roll it out in report-only mode first, then enforce a nonce-based strict policy.

What a CSP does

MDN describes CSP as a feature that helps prevent or minimize certain threats by restricting what a site's code may do. It is delivered in the Content-Security-Policy header as semicolon separated directives.

Content-Security-Policy: default-src 'self'; img-src 'self' example.com

Here default-src 'self' loads only same-origin resources by default, and images may also come from example.com.

How it helps against XSS

MDN calls CSP defense in depth. If an attacker gets script into your page, a CSP can still:

  • block script tags that point to malicious sources
  • disable inline script blocks, inline event handlers and javascript: URLs
  • disable eval and similar string-to-code functions

MDN stresses that it does not replace sanitizing input. Do both.

Step 1: report only

Test a policy without enforcing it using the report-only header.

Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint

Violations go to your reporting endpoint but nothing is blocked. MDN notes that a report-only policy cannot be delivered in a meta element. Read reports for a week or two and find the legitimate scripts you did not know about.

Step 2: a strict policy with nonces

MDN describes allowlists of hosts as hard to get right, because they often permit unsafe domains by accident. A strict CSP uses nonces or hashes.

Content-Security-Policy: script-src 'nonce-RANDOM'; object-src 'none'; base-uri 'none'

The server must create a new unpredictable nonce for every response, put it in the header and add the same nonce to each script tag it intends to run. Because the nonce changes each time, you need server-side templating. Static pages can use hashes instead, but those need recalculating whenever a script changes.

Add nonces only to your own script tags, so an attacker's injected scripts do not get one.

Step 3: enforce and keep watching

Switch the header from report-only to enforcing once reports are quiet. Keep the reporting endpoint, because a new third party script will show up as a violation. Treat that as a review prompt.

Common mistakes

  • Using unsafe-inline to make errors go away, which defeats the purpose.
  • Allowing wide host patterns.
  • Never reading the reports.

Combine a CSP with careful cookie settings. See cookie flags explained.

Handling third party scripts

Analytics, chat widgets and tag managers are the usual reason a CSP fails. Take stock of each one.

  • List every third party script and who approved it.
  • Remove the ones nobody can explain.
  • For the rest, load them through your nonce-bearing tag so they are covered by the policy, and check what further scripts they pull in.
  • Watch the reports after each marketing change. A new tag will show up as a violation, which is the policy doing its job.

A tag manager that lets non-engineers add arbitrary script can undo your whole policy. Decide who may use it and review changes.

Quick check

Can a CSP stop all XSS?

No. It reduces the impact. Keep sanitizing and encoding output.

Will it break my site?

It can, which is why report-only mode comes first.

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.