完全无效,因浏览器规范从未实现该机制;chrome 40+、firefox 50+、edge 14+均忽略它,必须由服务端通过http响应头设置x-frame-options或更推荐的content-security-policy frame-ancestors。

为什么写X-Frame-Options完全没用
浏览器规范从没实现过通过<meta http-equiv="X-Frame-Options">发送该指令。Chrome 40+、Firefox 50+、Edge 14+全部忽略它,页面照常被嵌入。这不是兼容性问题,是根本没这个机制。
常见错误现象:
- 在
里写了<meta http-equiv="X-Frame-Options" content="DENY">,但curl -I查响应头,压根没看到X-Frame-Options - 本地开发时看似生效,实则是Vite/Webpack Dev Server自动注入了响应头,误以为
meta起了作用
结论很直接:必须由服务端在HTTP响应头中发送。前端HTML结构再完整,也改不了这个事实。
Content-Security-Policy frame-ancestors语法踩坑清单
frame-ancestors是当前唯一被W3C推荐、主流浏览器强制执行的标准方案,但它语法极其敏感,错一个字符就退化成无防护状态。
关键规则:
-
'none'和'self'必须带单引号,写成frame-ancestors none或frame-ancestors self会直接失效 -
'self'不支持路径后缀,'self'/admin或'self' https://example.com/admin都是非法语法 - 允许多个域名时,用空格分隔,不要加逗号:
frame-ancestors 'self' https://embed.example.com https://dash.company.net - 不能使用通配符:
https://*.example.com不被识别,必须列全可信域名
有效示例:
Content-Security-Policy: frame-ancestors 'none';
Content-Security-Policy: frame-ancestors 'self' https://app.trusted-cdn.com;
服务端配置优先级与 fallback 策略
如果同时设置了X-Frame-Options和frame-ancestors,后者会生效,前者被忽略。现代浏览器(Chrome 79+、Firefox 69+)已将frame-ancestors作为主控机制。
但仍有两点必须考虑:
- IE11及更老版本不支持
frame-ancestors,只认X-Frame-Options,所以生产环境建议两者都配(X-Frame-Options作为降级兜底) - Nginx配置需加
always标志,否则304响应可能不带头:add_header X-Frame-Options "DENY" always; - 若用Express,需确保中间件在所有路由前执行,且不在静态文件中间件之后被覆盖
注意:ALLOW-FROM已被Chrome/Firefox废弃,别用。
HTML结构层唯一能做的兜底动作
服务端响应头是主防线,HTML结构层几乎做不了什么——除了极有限的JS遮罩,仅适用于旧版IE或沙箱iframe绕过CSP的极端场景。
重点不是“防嵌入”,而是“让嵌入后无法交互”:
- 在
开头立即插入一层全屏<div style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;background:#fff">,配合<code>pointer-events:none可临时遮挡 - 用JS检测
window.top !== window.self,触发时执行window.top.location = window.location跳转(但可能被沙箱iframe拦截) - 这种JS方案易被绕过,不能替代响应头,仅作最后一道视觉干扰
真正起效的永远只有服务端发出的Content-Security-Policy或X-Frame-Options。其他任何HTML内操作,本质都是补救,不是防御。











