CSP 必须通过 HTTP 响应头生效,<meta> 标签配置不可靠:因执行优先级低于响应头、file:// 协议下完全失效、不支持 report-to/frame-ancestors 等关键指令,且 Chrome 124+ 限制其 script-src 支持。

CSP 必须通过 HTTP 响应头生效,<meta http-equiv="Content-Security-Policy"> 在多数现代场景下不可靠,尤其当服务端已发送 CSP 头时会被忽略。
为什么标签配置CSP经常失效
浏览器对 Content-Security-Policy 的执行优先级是:HTTP 响应头 > <meta> 标签。只要服务器返回了任何 CSP 头(哪怕只是空值或语法错误),<meta> 就完全不参与策略计算。
- 本地用
file://协议打开 HTML 文件时,CSP 全面失效——<meta>和响应头都无效 - 开发服务器(如
http-server、Vite、Webpack Dev Server)默认不发 CSP 头,此时<meta>才可能起作用,但 Chrome 124+ 已开始限制其对script-src等关键指令的支持 -
<meta>无法设置report-to、base-uri、frame-ancestors等指令,这些在响应头中才完整支持
Nginx 中正确添加 CSP 响应头
必须在 server 或 location 块中使用 add_header,注意引号嵌套和空格不能出错:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://js.stripe.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' api.example.com; form-action 'self'; base-uri 'self'; frame-ancestors 'none'; report-to csp-endpoint";
- 双引号包裹整个策略值,内部单引号用于源关键字(如
'self') - 多个指令用分号分隔,末尾不加多余空格或分号
- 若策略含动态内容(如 nonce),需用 Nginx 变量或交由后端注入,
add_header本身不支持运行时生成 - 确认响应头实际发出:Chrome DevTools → Network → HTML 请求 → Response Headers → 查看是否存在
content-security-policy(全小写)
script-src 中禁用内联脚本的实操要点
只写 script-src 'self' https://cdn.example.com 并不能阻止 <script>alert(1)</script> 或 <button onclick="do()">,因为默认允许内联——必须显式排除。
立即学习“前端免费学习笔记(深入)”;
- 去掉
'unsafe-inline'是第一步,但会导致合法内联脚本白屏 - 用 nonce:后端每次响应生成唯一随机字符串(如
abc123def456),写入响应头script-src 'nonce-abc123def456',并在对应<script nonce="abc123def456">中重复该值 - 用 hash:对静态内联脚本内容做 SHA256,转 Base64,如
sha256-BzLdO9X...;动态拼接的 JS(如模板字符串注入)无法预计算 hash,不能用 -
'strict-dynamic'可配合 nonce 使用,让被信任的根脚本加载的子脚本自动获得许可,但会忽略'self'等源列表,需谨慎评估依赖链
容易被忽略的指令和隐形风险
很多人只关注 script-src,但攻击者常从其他入口突破:
-
base-uri 'none'或'self'必须显式设置,否则恶意<base href="http://attacker.com">会让所有相对路径请求被劫持 -
form-action 'self'缺失时,表单可提交到任意地址,成为 CSRF 或数据外泄通道 -
frame-ancestors 'none'不设,页面可被嵌入钓鱼 iframe,触发点击劫持 -
connect-src控制fetch、XMLHttpRequest、sendBeacon目标,漏配会导致 API 调用被静默拦截,控制台只报 CSP 错误,无网络失败提示 - 所有指令都受
default-src影响,但script-src、style-src、base-uri等关键指令绝不能依赖 fallback,必须单独声明
真正难的不是写出一条 CSP,而是让每条指令在真实页面生命周期里持续有效:第三方 SDK 更新、前端构建产物哈希变化、CDN 域名轮换、甚至 eval() 的间接调用(比如某些 UI 框架的模板编译),都会导致策略突然失效或安全缺口。上线前必须用 Content-Security-Policy-Report-Only 模式跑至少 3 天,收集报告再切正式策略。



















