Laravel默认不设X-Frame-Options是为优先采用标准CSP的frame-ancestors,因现代浏览器已忽略X-Frame-Options;推荐用spatie/laravel-csp配置frame_ancestors并确保全局生效。

单独设置 X-Frame-Options 在 Laravel 中不仅无效,还可能掩盖真实风险——现代浏览器(Chrome 79+、Firefox 69+、Edge 79+)已完全忽略它,当 Content-Security-Policy 的 frame-ancestors 存在时,X-Frame-Options 会被静默丢弃。
为什么 Laravel 默认不设 X-Frame-Options?
Laravel 本身不自动注入 X-Frame-Options,这不是疏忽,而是有意为之:框架选择将防护交由更标准、更可控的 CSP 控制。如果你用 php artisan serve 或 Nginx/Apache 部署,响应头里没有 X-Frame-Options 是正常现象;但若你手动加了它(比如在中间件里),反而容易和后续引入的 CSP 冲突。
- 框架内置的
TrustProxies、PreventRequestsDuringMaintenance等中间件均不处理帧嵌套控制 - 第三方包(如
spatie/laravel-csp)默认也不启用frame-ancestors,需显式配置 - 运行
curl -I https://yoursite.com查看响应头,若没看到X-Frame-Options或Content-Security-Policy,说明防护尚未生效
正确做法:用 CSP 的 frame-ancestors 替代
在 Laravel 中启用点击劫持防护,唯一推荐路径是配置 Content-Security-Policy: frame-ancestors。使用 spatie/laravel-csp 是最稳妥的方式:
- 执行
composer require spatie/laravel-csp - 发布配置:
php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider" - 编辑
config/csp.php,修改frame_ancestors配置项:
'frame_ancestors' => [
'none', // 禁止所有嵌入,等价于 X-Frame-Options: DENY
// 或 'self', // 仅同源
// 或 ['https://trusted.example.com', 'https://app.company.com'], // 多个明确白名单
],
- 确保中间件
\Spatie\Csp\AddCspHeaders::class已注册到app/Http/Kernel.php的$middleware或$middlewareGroups['web']中 -
'self'后不能带路径(如'self'/admin不合法),也不支持通配符(https://*.example.com会失效)
Laravel 中误用 X-Frame-Options 的典型坑
很多团队在中间件里硬塞 X-Frame-Options,结果既没防住攻击,又埋下兼容隐患:
- 在
App\Http\Middleware\TrustProxies或自定义中间件中写$response->headers->set('X-Frame-Options', 'DENY')—— 这会让响应同时携带X-Frame-Options和Content-Security-Policy,语义冗余且增加维护成本 - 依赖前端 JS 的
if (window !== top) top.location = window.location—— 完全无效:攻击者可禁用 JS,或用sandbox="allow-scripts"绕过 - 只给 HTML 响应加头,却漏掉 API 接口(如
/api/user返回 JSON)、错误页(404.blade.php)、文件下载响应 —— 这些都可能被 iframe 加载并用于劫持 - 使用
ALLOW-FROM https://xxx.com—— Chrome/Firefox 已彻底废弃该值,实际行为等同于未设置
验证是否真正生效的关键检查点
别只信配置文件,必须实测:
- 打开 DevTools → Network → 刷一次页面 → 找任意一个 HTML 响应 → 查看 Response Headers 是否含
Content-Security-Policy: frame-ancestors 'none'(注意单引号不能少) - 本地建一个测试 HTML:
<iframe src="https://yoursite.com"></iframe>,在 Chrome/Firefox 中打开,确认 iframe 显示为空白或报错Refused to display 'https://yoursite.com/' in a frame because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'none'" - 若仍能加载,检查是否被 CDN(如 Cloudflare)或反向代理(Nginx)覆盖了响应头 —— 这类中间层常默认清除或重写安全头
- IE11 用户极少,除非你明确要支持它,否则不必保留
X-Frame-Options作为兜底;加了反而可能干扰 CSP 解析逻辑
最易被忽略的是:API 响应、图片/CSV 下载、500 错误页这些非主页面资源,同样需要 CSP 头——spatie/laravel-csp 默认只作用于 text/html 响应,其他类型得手动扩展中间件逻辑或改用全局响应拦截。


















