不喜欢广告? 无广告 今天

Content Security Policy Writing a CSP Header That Actually Works

发布日期
Content Security Policy: Writing a CSP Header That Actually Works 1
广告 移除?

Most developers know they should add a Content Security Policy header to their apps. Few have one that’s actually doing anything useful. Either it’s so permissive it defeats the purpose, or it breaks half the site and gets removed in frustration.

This guide walks through what CSP does, which directives matter, and how to write a header that blocks real attacks without breaking your app.

What CSP Actually Does

A Content Security Policy tells the browser where it’s allowed to load resources from. Script from this domain only. Styles from that CDN. Images from anywhere. No inline scripts.

When a browser receives a CSP header, it enforces those rules before executing anything. If a cross-site scripting (XSS) attack injects a malicious script into your HTML, CSP blocks it at the browser level — even if the script got past your input sanitization.

That’s the core value: CSP is your second line of defense when input validation fails.

The Directives That Matter

CSP has dozens of directives, but most production headers only need six:

Directive What it restricts Common mistake
default-src Fallback for any resource type not explicitly listed 环境 'self', then forgetting fonts and frames aren’t covered
script-src JavaScript source URLs and inline execution Adding 'unsafe-inline' to silence console errors
style-src CSS source URLs and inline style blocks Forgetting your CDN or inline styles injected by JS libraries
img-src Image sources including data URIs Missing data: for base64 images, blob: for canvas exports
connect-src XHR, fetch, WebSocket, and EventSource destinations Forgetting analytics endpoints and API subdomains
frame-ancestors Which origins can embed your site in an <iframe> Skipping it entirely — leaving clickjacking wide open

frame-ancestors replaces the older X-Frame-Options header and is worth adding even before you have full CSP coverage elsewhere.

为什么 unsafe-inline Destroys the Point

When you add 'unsafe-inline'script-src, you’re telling the browser: execute any script tag you find, wherever it came from. That includes scripts injected by XSS attacks.

'unsafe-eval' is worse — it allows eval(), Function(),并且 setTimeout("string"), which are common attack vectors even in otherwise-clean codebases.

A policy with either of these documents your sources without providing any meaningful injection protection. The attacker doesn’t care where your legitimate scripts load from if they can inject their own inline.

The Right Way: Nonces and Hashes

If you have legitimate inline scripts, you have two options that don’t surrender your policy:

Nonces generate a unique token per page load. The server adds it to the CSP header and to the inline script’s nonce attribute. Only scripts with a matching nonce execute.

<!-- CSP header -->
Content-Security-Policy: script-src 'nonce-abc123xyz'

<!-- Inline script with matching nonce -->
<script nonce="abc123xyz">
  window.analyticsId = '...';
</script>

Hashes compute a SHA-256 fingerprint of the exact script content. Add that hash to script-src and the browser runs that specific script — but nothing else.

Content-Security-Policy: script-src 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc='

Nonces work better for dynamic pages where content changes per request. Hashes suit static scripts that never change between deploys.

Deploy with Report-Only Mode First

Adding CSP to an existing site without testing is how you break it in production. Start with the report-only header instead:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-{random}'; report-uri /csp-violations

The browser applies the rules and reports violations — it doesn’t block anything yet. That violation log tells you exactly what you need to add to the policy before you flip it to enforcement mode.

Most CSP deployments that break sites skipped this step.

A Real CSP for a Typical SaaS App

Here’s a production-ready header for an app using a CDN, Google Analytics, and Stripe. Each directive is annotated:

Content-Security-Policy:
  # Tight default: only load from your own origin
  default-src 'self';

  # Scripts: your origin, GA tag manager via nonce, Stripe.js
  script-src 'self' https://www.googletagmanager.com https://js.stripe.com 'nonce-{SERVER_NONCE}';

  # Styles: your origin and Google Fonts CSS
  style-src 'self' https://fonts.googleapis.com;

  # Fonts: your origin and Google Fonts CDN
  font-src 'self' https://fonts.gstatic.com;

  # Images: your origin, CDN subdomain, and base64 data URIs
  img-src 'self' https://cdn.yourdomain.com data:;

  # Fetch/XHR: your API, GA collection endpoint, Stripe API
  connect-src 'self' https://www.google-analytics.com https://api.stripe.com;

  # Stripe renders its fields in an iframe
  frame-src https://js.stripe.com;

  # Nobody should be framing your app
  frame-ancestors 'none';

  # Block <object> and <embed> entirely
  object-src 'none';

  # Force HTTP requests to upgrade to HTTPS
  upgrade-insecure-requests;

{SERVER_NONCE} with a cryptographically random value generated per request — e.g., base64_encode(random_bytes(16)) in PHP or crypto.randomBytes(16).toString('base64') in Node.

Common CSP Mistakes

Wildcards that are too broad. script-src * allows scripts from any origin. You might as well have no script policy at all.

Forgetting subdomains. 'self' only covers your exact origin. https://api.yourdomain.com is a different origin and needs an explicit entry under connect-src.

Skipping frame-ancestors. Clickjacking protection is independent of XSS protection. Add it separately from the rest of your directive set.

Setting CSP in multiple places. If your CDN and your app both set a CSP header, behavior gets unpredictable. Set it in one layer — usually your origin server or reverse proxy.

使用 report-uri without a handler. Violations generate POST requests to your endpoint on every affected page load. Either handle them or point to a service like Report URI.

Build Your Header Without the Guesswork

Tracking every domain your page loads resources from across scripts, styles, fonts, images, and API calls gets tedious quickly. An online CSP header generator lets you configure each directive visually and outputs a production-ready header you can paste directly into your server config.

Use it as a starting point, then audit your actual network requests in DevTools to catch anything the generator missed.


A CSP that’s too loose is decoration. One that’s too strict breaks your users. Start in report-only mode, tighten from there, and use nonces where inline scripts are unavoidable. Your policy should make an attacker’s job harder — not yours.

想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?