直接写 CSP 响应头易覆盖 Laravel 默认安全头、难以动态控制 nonce/report-uri 且多中间件可能冲突;Spatie/laravel-csp 未生效需检查中间件注册位置、DEBUG 模式禁用及头覆写问题;内联脚本应优先用 nonce 或 hash 而非 unsafe_inline;connect-src 需单独配置外部 API;建议先用 Report-Only 模式收集日志再启用强制策略。

为什么直接写 Content-Security-Policy 响应头会出问题
手动在中间件或 App\Http\Kernel 里用 header() 或 response()->withHeaders() 添加 CSP 头,容易覆盖 Laravel 自带的 X-Frame-Options、X-Content-Type-Options 等安全头,也难以动态控制内联脚本、nonce、report-uri 等复杂策略。更关键的是:Laravel 的响应生命周期中,多个中间件可能重复设置同名头,导致策略被截断或解析失败。
用 Spatie/laravel-csp 注册中间件后没生效?检查这三点
安装后运行 php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider" 生成配置文件 config/csp.php,但仅此不够:
- 确认
app/Http/Kernel.php的$middlewareGroups['web']中已加入\Spatie\Csp\AddCspHeaders::class(不是globalmiddleware,否则 API 路由也会被强制加头) - 检查是否启用了
APP_DEBUG=true:该包默认在 debug 模式下禁用 CSP(避免开发时白屏),需显式设置'enabled' => env('CSP_ENABLED', true)并在.env中设CSP_ENABLED=true - 确认没有其他中间件(如自定义安全头中间件、CDN 缓存中间件)在
AddCspHeaders之后又覆写了Content-Security-Policy头
csp.php 配置里 'scripts' => [] 和 'unsafe_inline' 怎么选
允许内联脚本最常见需求是 Vue/Alpine 的 @click 或 Blade 中的 @stack('scripts'),但 unsafe_inline 是高危选项,应优先用 nonce 或 hash:
- 启用 nonce:在
config/csp.php中设'scripts' => ['self', 'unsafe_inline']→ 不安全;正确做法是删掉unsafe_inline,改用'scripts' => ['self', 'nonce-,并在 Blade 模板中给{{ csp_nonce() }}']加上nonce属性 - 用 hash 替代:对已知内联脚本内容做 SHA256,例如
对应'sha256-abc123...',填入'scripts' => ['self', 'sha256-abc123...'];但每次修改脚本都要重算 hash,适合静态片段 - 第三方 JS(如 Google Analytics)必须显式添加域名:
'scripts' => ['self', 'https://www.googletagmanager.com'],不能只写googletagmanager.com(协议和端口必须匹配)
如何让 CSP 允许 fetch() 请求到外部 API(比如 Stripe)
CSP 的 connect-src 控制 fetch、XMLHttpRequest、EventSource 等连接行为,和 script-src 是分开的:
return [
'connect-src' => [
'self',
'https://api.stripe.com',
'https://hooks.stripe.com',
],
];
注意:connect-src 不继承 script-src 的值;如果漏配,浏览器控制台会报 Refused to connect to 'https://api.stripe.com' because it violates the following Content Security Policy directive;另外,localhost 开发环境需单独加上 'http://localhost:8000'(协议不能省)。
report-uri 和 report-to 在现代浏览器中行为不一致,且上报 endpoint 需要能接收 POST JSON 并防滥用,实际项目中建议先用 Content-Security-Policy-Report-Only 头跑一周收集违规日志,再切到 enforce 模式——这点很容易被跳过。










