必须用HTTP响应头配置CSP,不能依赖标签;因生效滞后、不支持frame-ancestors等关键指令,且易被响应头静默覆盖,生产环境基本无效。

别在 <meta> 标签里配 CSP——它拦不住首屏脚本、不支持 frame-ancestors、会被响应头静默覆盖,生产环境基本无效。
必须用 HTTP 响应头,不能靠 <meta>
浏览器收到第一个字节时就解析响应头,而 <meta http-equiv="Content-Security-Policy"> 要等 HTML 解析到 才生效。此时前面的 <script></script>(比如 SSR 注入的初始化代码)早已执行完毕。
-
frame-ancestors、report-to、upgrade-insecure-requests在<meta>中完全不生效 - Android WebView 可能跳过含
nonce-的规则,导致白屏 - 重定向页、
<iframe></iframe>子页、data:URI 都不受<meta>约束 - 只要服务端发了任何
Content-Security-Policy响应头(哪怕值为空),<meta>就被忽略
script-src 写错等于没设
94% 的线上 CSP 失效源于 script-src 松动或遗漏。它不是“允许哪些 CDN”,而是“只允许从哪些来源执行 JS”——包括 onclick、eval()、new Function()、document.write() 生成的脚本。
- 绝对禁用
'unsafe-inline'和'unsafe-eval':加一个,整个 XSS 防护就形同虚设 - Webpack/Vite 构建产物若含内联
<script></script>(如 SSR 模板注入),必须配nonce,不能放宽策略 -
script-src 'self' 'strict-dynamic'是错的:'self'会让浏览器忽略'strict-dynamic';正确写法是script-src 'nonce-abc123' 'strict-dynamic',且首个执行脚本必须带该nonce - 第三方 SDK(如埋点、广告)的域名必须显式列入,漏一个就可能被拦截或绕过
Nonce 必须动态生成且两端严格一致
控制台报 Refused to execute inline script,往往不是标签写错了,而是两端没对齐。
立即学习“前端免费学习笔记(深入)”;
- nonce 值必须由服务端每次 HTTP 响应时生成,推荐 32 字节以上十六进制(如
27f434278e6b79578d5c9f0a321c4567),不能硬编码、不能复用 - 响应头中必须写全:
Content-Security-Policy: script-src 'self' 'nonce-27f434278e6b79578d5c9f0a321c4567' - 对应
<script></script>标签必须显式携带相同值:<script nonce="27f434278e6b79578d5c9f0a321c4567"></script>,大小写敏感,不能有空格或换行 - SSR 框架 hydration 过程中不能覆盖服务端已注入的
nonce属性
media-src 和 connect-src 容易被忽略
HTML5 视频播放器(如 Video.js、hls.js)出问题,常因媒体资源加载路径没被策略覆盖。
-
media-src控制<video src></video>、poster、<track src></track>,必须显式列出可信 HTTPS 域名,不能依赖'self'回退 - HLS/DASH 分片(
.m3u8、.ts)是通过fetch/XHR加载的,归connect-src管,不是media-src -
poster图片、错误占位图属于图片资源,受img-src约束,不是media-src - 若用 Vimeo 或 YouTube embed,需同时配
frame-src和media-src,并确认其支持 HTTPS 与 CORS
真正关键的不是“写了 CSP”,而是 script-src 是否覆盖所有动态加载入口、nonce 是否每次请求唯一、media-src 和 connect-src 是否协同到位——漏掉任何一个环节,攻击链就可能绕过防线。



















