← Back to Blog

Most CSP headers use unsafe-inline and call it security. Here's what strict actually looks like.

Most CSP headers use unsafe-inline and call it security. Here's what strict actually looks like.

If your Content Security Policy includes 'unsafe-inline' in the script-src directive, you don't have a Content Security Policy. You have a header that makes security auditors feel good while doing nothing to stop cross-site scripting.

That's not an exaggeration. The entire point of CSP for scripts is to prevent the browser from executing injected code. 'unsafe-inline' tells the browser to execute any inline script it finds. An attacker who injects <script>steal(document.cookie)</script> into your page wins, because your CSP explicitly allows it.

The three layers of strict CSP

Nonce-based scripts. Instead of allowing all inline scripts, you generate a random nonce on each page load and add it to both the CSP header (script-src 'nonce-abc123') and each legitimate script tag (<script nonce="abc123">). Injected scripts won't have the nonce. The browser blocks them.

strict-dynamic. Once you have nonces, 'strict-dynamic' tells the browser that any script loaded by a nonced script is also trusted. This means you don't need to list every CDN domain in your CSP. Your nonced bootstrap script can load whatever it needs, but an attacker's injected script still can't execute.

Trusted Types. This is the newest layer. require-trusted-types-for 'script' tells the browser to block DOM XSS sinks like innerHTML, document.write, and eval unless the input has been explicitly sanitized through a Trusted Types policy. Even if an attacker gets past your CSP, Trusted Types blocks the most common DOM manipulation attacks.

Why most sites don't use any of these

Because strict CSP is hard. Adding nonces requires server-side rendering or edge-function injection on every page load. strict-dynamic requires understanding your script dependency chain. Trusted Types requires auditing every line of JavaScript that touches the DOM.

And because 'unsafe-inline' plus a list of CDN domains passes most automated security scanners. The scanner sees a CSP header, checks the box, and moves on. Nobody manually verifies that the policy actually prevents anything.

What the tool checks

The CSP Strictness Audit fetches your page and its CSP, then evaluates six dimensions:

  1. Whether nonces or hashes are used instead of 'unsafe-inline'
  2. Whether 'strict-dynamic' is present
  3. Whether 'unsafe-eval' is absent
  4. Whether report-to or report-uri is configured so you know when violations happen
  5. Whether Trusted Types enforcement is enabled
  6. Whether wildcard sources are present and how broad they are

The result is a strictness score with specific, actionable findings per dimension.

Getting these right from the start is easier than retrofitting. If you're setting up new sites regularly, The $97 Launch walks through the security headers to ship with on day one.

Fact-check notes and sources

Related reading

This post is informational, not security-consulting advice. Mentions of Google, W3C, and MDN are nominative fair use. No affiliation is implied.

← Back to Blog

Accessibility Options

Text Size
High Contrast
Reduce Motion
Reading Guide
Link Highlighting
Accessibility Statement

J.A. Watte is committed to ensuring digital accessibility for people with disabilities. This site conforms to WCAG 2.1 and 2.2 Level AA guidelines.

Measures Taken

  • Semantic HTML with proper heading hierarchy
  • ARIA labels and roles for interactive components
  • Color contrast ratios meeting WCAG AA (4.5:1)
  • Full keyboard navigation support
  • Skip navigation link
  • Visible focus indicators (3:1 contrast)
  • 44px minimum touch/click targets
  • Dark/light theme with system preference detection
  • Responsive design for all devices
  • Reduced motion support (CSS + toggle)
  • Text size customization (14px–20px)
  • Print stylesheet

Feedback

Contact: jwatte.com/contact

Full Accessibility StatementPrivacy Policy

Last updated: April 2026