symfony不会自动设置content-security-policy头,必须通过kernel.response事件监听器统一注入;仅设default-src 'self'会导致功能异常,需按路由差异化配置script-src、style-src等,并配合nonce或hash支持内联资源。

Symfony 不会自动设置 Content-Security-Policy 响应头,不配就等于没防护——浏览器根本不会执行任何 CSP 限制。
为什么不能在控制器里硬写 $response->headers->set()
策略分散在各控制器里会导致维护困难、环境差异难统一、nonce 无法全局生成、内联脚本/样式容易漏加白名单。更关键的是:一旦某个控制器忘了加,那页就裸奔。
- 策略必须集中定义,比如放在事件监听器或响应修饰器中
- 推荐监听
kernel.response事件,在响应发出前统一注入头 - 开发阶段务必先用
Content-Security-Policy-Report-Only头收集违规日志,再切到阻断模式 - 不要复用同一段字符串拼接逻辑——不同路由(如
/admin和/public)往往需要差异化策略
default-src 'self' 是起点,不是终点
只写 default-src 'self' 看似严格,但实际会让所有外部 CDN 脚本、字体、图片、AJAX 请求全部失败,页面直接白屏或功能异常。
- 先收紧:从
default-src 'none'开始测试,逐步放开必要指令 - 再细化:显式配置
script-src、style-src、img-src等,避免依赖default-src的隐式继承 - 内联资源必须配合 nonce 或 hash:比如 Twig 中用
{{ csp_nonce('script') }}(需自建服务或扩展支持),否则'unsafe-inline'就是开后门 - 动态生成的 JS(如 Webpack HMR、React DevTools 注入)在 dev 环境需额外允许
http://localhost:*,但 prod 必须移除
和 X-Frame-Options 共存时,frame-ancestors 会覆盖前者
如果你同时设置了 X-Frame-Options: DENY 和 Content-Security-Policy: frame-ancestors 'none',现代浏览器只认后者,前者被忽略。这不是 bug,是 CSP 规范明确规定的优先级。
- 建议停用
X-Frame-Options,统一用frame-ancestors管理嵌套策略 -
frame-ancestors 'self'允许同域 iframe,frame-ancestors 'none'完全禁止(等价于X-Frame-Options: DENY) - 若需兼容 IE10 及更老浏览器,才保留
X-Frame-Options作为降级方案 - Nginx 中合并 CSP 头时注意:用
add_header ... always,否则 304 响应可能丢失头
生产环境最容易被忽略的三个点
很多团队配完 CSP 就以为万事大吉,结果上线后 JS 报错、图片不显示、AJAX 403,问题出在细节上。
-
connect-src必须包含 WebSocket 地址(如wss://api.example.com),否则前端实时通信直接中断 - 使用第三方登录(Google/Facebook SDK)、埋点(Sentry、Plausible)、广告(Google AdSense)时,对应域名必须显式加入
script-src和frame-src(CSP3 中已改名child-src) - PHP 输出缓冲或反向代理(如 Nginx、Cloudflare)可能缓存并覆盖你 set 的头——检查最终响应是否真包含你写的
Content-Security-Policy











