VulnerabilityScanPro
All articles
// ENGINEERING

Why your CSP probably allows unsafe-inline

If your team has shipped a CSP, there's an 80% chance it includes `'unsafe-inline'` or `'unsafe-eval'`. Here's why that defeats the point — and how to migrate to nonces without breaking your analytics.

Vulnerabilityscanpro admin
Vulnerabilityscanpro admin
Apr 14, 2026 5 min read
Why your CSP probably allows unsafe-inline

The pattern

A team adds CSP. Their app breaks. They add 'unsafe-inline' to script-src. The app works. Six months later the security review flags the CSP as effectively useless. They don't believe it.

They should.

What unsafe-inline actually does

It tells the browser: trust any inline <script> block, no questions. Including any script an attacker injects via XSS. The CSP becomes a thin compliance fig leaf.

The migration that always works

  1. Audit your inline scripts. Most will be analytics snippets, third-party widgets, or one-off bootstrap code.
  2. Add a nonce to every legitimate inline script: <script nonce="{{ $nonce }}">. Generate the nonce per-request.
  3. Update CSP to allow 'nonce-{{ $nonce }}' instead of 'unsafe-inline'.
  4. Test in report-only mode firstContent-Security-Policy-Report-Only with a reporting endpoint catches anything you forgot.
  5. Flip to enforcing.

What to do about third-party scripts

Most analytics vendors (Google Analytics, Plausible, Fathom) ship with a script that doesn't need inline. The third-party domains go in script-src. Inline tracking pixels typically don't need CSP relaxation — they're <img> tags.

What we look for in audits

When we review CSP, we check three things:

  • Is 'unsafe-inline' present without nonce/hash fallback? Critical.
  • Is 'unsafe-eval' present? High — almost always avoidable with bundler config.
  • Are wildcard domains in script-src? Medium — supply-chain risk.

A real CSP closes about 70% of XSS exploit paths even when other layers fail.


// tags: #csp #headers #browser