Spatie/laravel-csp 是 Laravel 中配置 CSP 的可靠方案,需正确注册中间件、关闭调试模式、使用 nonce 替代 unsafe-inline,并显式声明第三方资源与 report-uri。

直接在 Laravel 中硬写 Content-Security-Policy 响应头,大概率导致白屏、JS/CSS 加载失败、第三方资源被拦截,且会覆盖 Laravel 自带的安全头(如 X-Content-Type-Options);用 Spatie/laravel-csp 是更可靠的选择,但中间件注册错位置、APP_DEBUG=true、或头被后续中间件覆写,都会让它“看起来没生效”。
中间件注册位置必须是 $middlewareGroups['web']
装完包、跑完 php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider" 后,CSP 还是不出现在响应头里?最常见原因是中间件没加对地方。
- ✅ 正确做法:打开
app/Http/Kernel.php,在$middlewareGroups['web']数组里追加\Spatie\Csp\AddCspHeaders::class - ❌ 错误做法:加到全局
$middleware数组——API 路由也会被强加 CSP,前端发请求可能因connect-src未配而失败 - ⚠️ 注意命名空间:Laravel 10+ 的类路径是
\Spatie\Csp\AddCspHeaders,不是旧版的Spatie\LaravelCsp\AddCspHeaders
APP_DEBUG=true 时 CSP 默认不启用
Spatie/laravel-csp 在调试模式下会静默禁用策略,避免开发时一写错就白屏。这不是 bug,是设计行为。
- 检查
.env:确认APP_DEBUG=false,或显式设CSP_ENABLED=true - 检查
config/csp.php:确保'enabled' => env('CSP_ENABLED', true),否则APP_DEBUG=true时即使CSP_ENABLED=true也无效 - 验证方式:临时在 Blade 模板中加
{{ config('csp.enabled') ? 'on' : 'off' }}看是否为on
内联脚本不能靠 unsafe-inline 过关
Vue/Alpine 的 @click、Blade 中的 @stack('scripts') 或直接写的 <script>console.log()</script>,默认会被 CSP 拦住。但启用 'unsafe-inline' 是高危操作,上线前必须替换。
- ✅ 推荐方案:用 nonce。在
config/csp.php中把'scripts' => ['self', 'nonce-{{ csp_nonce() }}'],然后模板中写<script nonce="{{ csp_nonce() }}">...</script> - ⚠️ 注意:
csp_nonce()是 Spatie 包提供的辅助函数,仅在启用了 nonce 支持时有效;它每次请求生成唯一值,不可缓存 - ⛔ 别写成
'scripts' => ['self', 'unsafe-inline']—— 浏览器会忽略整条script-src指令(因同时含nonce和unsafe-inline)
第三方资源和 report-uri 必须显式配置
CSP 是白名单机制,没列进来的域名一律拒绝。Google Analytics、CDN 上的 jQuery、字体服务等,都得手动加;报告地址不配好,你根本不知道哪条策略写错了。
-
script-src示例:['self', 'https://www.googletagmanager.com', 'https://cdn.jsdelivr.net']—— 必须带协议和完整域名,googletagmanager.com不行 -
font-src示例:['self', 'https://fonts.googleapis.com', 'https://fonts.gstatic.com'] -
report_uri需配合后端路由:'report_uri' => '/csp-report',再定义Route::post('/csp-report', [CspReportController::class, 'store'])->withoutMiddleware(['VerifyCsrfToken']) - 开发阶段建议先开
report_only => true,看日志再收紧,别一上来就强制拦截
真正难的不是写那几行配置,而是每次加一个新 JS 库、换一次 CDN、改一段内联逻辑,都得回头核对 CSP 是否还匹配——它不像日志或缓存,出问题不会报错,只会静默失败。


















