iframe 嵌入检测
免费在线 iframe 检测工具:输入任意网页 URL,检查其 X-Frame-Options 与 Content-Security-Policy frame-ancestors 响应头,判断页面是否允许被 iframe 嵌套,并通过实机预览二次确认。检测历史仅保存在当前标签页会话内。
检测历史(当前会话)
仅保存在当前标签页(sessionStorage),关闭即清除,不上传服务器。
检测原理
- 输入目标 URL 并点击检测,服务端以真实 iframe 请求(携带 Sec-Fetch-Dest: iframe)获取该页面的响应头信息。
- 依据 X-Frame-Options(DENY / SAMEORIGIN 会拦截)与 Content-Security-Policy frame-ancestors(仅 * 或纯协议通配才允许任意第三方父页面)给出结论。
- 同一 URL 同时加载到下方实机预览 iframe 中,确认浏览器的真实表现,包括响应头无法反映的 JavaScript frame-busting 行为。
页面能否被嵌入由什么决定?
一个网页能否出现在其他网站的 <iframe> 里,由浏览器依据页面自身返回的响应头决定。本工具在服务端完成同样的检查,并立即给出明确结论。
X-Frame-Options 是经典响应头:DENY 禁止一切页面框架嵌套,SAMEORIGIN 只允许同源页面嵌套,而 ALLOW-FROM 已被现代浏览器移除。该响应头缺失时,旧规则不会拦截嵌入。
Content-Security-Policy 的 frame-ancestors 是现代替代方案,用于列出允许嵌入本页的来源,例如 frame-ancestors 'self' https://example.com。取值为 *(或 https: 这样的纯协议通配)时允许任意 HTTPS 父页面嵌入;范围更窄则禁止第三方嵌入。
响应头并不是全部:页面脚本可能执行 frame-busting(比较 top !== self 后强制顶层跳转),登录页或地区受限接口对数据中心请求也可能返回不同内容。因此每条结论都配有实机预览作为最终确认。
快速决策:响应头配置
如果你是站点所有者,可通过响应头直接控制页面是否允许被嵌入。
禁止被任何页面嵌入
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
浏览器会拒绝在所有 iframe 中渲染该页面,是点击劫持防护的最严格配置。
仅允许同源页面嵌入
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
只允许协议、主机、端口完全一致的页面嵌入,第三方站点无法嵌套。
仅允许指定来源嵌入
Content-Security-Policy: frame-ancestors 'self' https://trusted.example.com
在 frame-ancestors 中列出可信来源,未列出的站点会被拦截。
允许任意页面嵌入
不设置 X-Frame-Options
Content-Security-Policy: frame-ancestors *
任何网站都可以在 iframe 中嵌入该页面,需自行评估安全风险。
提示:X-Frame-Options 已被 CSP frame-ancestors 取代,两者同时存在时以 CSP 为准;建议优先使用 frame-ancestors。
服务端如何检测请求来自 iframe?
大约从 2020 年起,基于 Chromium 与 Firefox 的浏览器会给每一次导航请求和子资源请求自动附加一组 Fetch Metadata 请求头。对判断嵌入场景最有用的是 Sec-Fetch-Dest:它无需请求页面配合,就能告诉服务端这次请求是在什么上下文里发起的。
- Sec-Fetch-Dest: document — 顶层页面导航,不在任何 frame 内。
- Sec-Fetch-Dest: iframe — 请求由 <iframe> 元素加载。
- Sec-Fetch-Dest: frame — 由旧式 <frame>(frameset)加载。
- Sec-Fetch-Dest: embed / object — 由 <embed> 或 <object> 元素加载。
本工具发起探测请求时会显式带上 Sec-Fetch-Dest: iframe,与浏览器真实嵌入页面时发出的请求完全一致,因此得到的结论反映的是目标服务端对真实 iframe 请求的实际行为,而不是一次普通抓取。
Sec-Fetch-Dest 是有用的参考信号,但不是安全边界:它只由现代浏览器发送,curl、爬虫、部分 WebView 以及旧版浏览器完全可以不带这个头,或者带上任意取值。服务端可以拿它做日志分析或限流参考,但不能仅凭它判断嵌入是否安全——这个决定权始终属于 X-Frame-Options 和 CSP frame-ancestors,浏览器会独立于请求头强制执行这两者。
在客户端,页面也可以不依赖任何响应头,直接检测自己是否运行在 frame 里,例如比较 window.top !== window.self(同源 frame 也可读取 window.frameElement),再据此反应——弹出提示或强制跳转到顶层。这正是前文提到的 frame-busting 技术,它不受请求头影响、始终有效。
服务端禁止 iframe 嵌入配置示例
以下展示如何在常见服务端 / 框架中配置完全禁止 iframe 嵌入(X-Frame-Options: DENY + CSP frame-ancestors 'none'),是上文快速决策中最严格的一种方案,可直接复制使用。
server {
location / {
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none'" always;
}
}
<IfModule mod_headers.c>
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none'"
</IfModule>
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
next();
});
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'Content-Security-Policy', value: "frame-ancestors 'none'" },
],
},
];
},
};
class SetFrameOptions
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('Content-Security-Policy', "frame-ancestors 'none'");
return $response;
}
}
export default {
async fetch(request) {
const response = await fetch(request);
const headers = new Headers(response.headers);
headers.set('X-Frame-Options', 'DENY');
headers.set('Content-Security-Policy', "frame-ancestors 'none'");
return new Response(response.body, { status: response.status, headers });
},
};
如果不是要完全禁止,只是想放行自己站点或少量可信来源,把 DENY 换成 SAMEORIGIN、'none' 换成 'self' 或具体的来源列表即可,其余配置方式不变。
常见平台踏坑表
除了通用的响应头规则,一些知名平台还有各自的特殊限制,不要只凭响应头就假定「可嵌入」或「禁止嵌入」。
| 平台 / 场景 | 实际表现 | 原因 |
|---|---|---|
| YouTube 视频页(youtube.com/watch) | 禁止嵌入 | 普通观看页发送了限制性框架策略,应改用官方 youtube.com/embed/{id} 播放器地址。 |
| Google 地图详情页 | 禁止嵌入 | 只有 Maps Embed API 提供的 iframe 地址(google.com/maps/embed)才允许被嵌入。 |
| Google / Microsoft 登录授权页 | 禁止嵌入 | 自 2015 年起被有意禁止在 iframe 中弹出,防止凭证钓鱼;登录流程必须在顶层窗口或弹窗中完成。 |
| 支付结算页(Stripe Checkout、PayPal、多数网银) | 禁止嵌入 | 支付表单被嵌入是典型的点击劫持攻击手段,支付方直接拒绝框架嵌套。 |
| GitHub 仓库 / 文件页面 | 禁止嵌入 | 全站设置了 frame-ancestors 'none',应改用 REST/GraphQL API 或截图替代 iframe。 |
| Notion / Google 文档「发布到网络」页面 | 可嵌入 | 产品设计上就是为嵌入而生,官方还提供了现成的嵌入代码。 |
| Wikipedia 词条页面 | 可嵌入 | 默认没有限制性的 X-Frame-Options/CSP,但大规模嵌入前需注意授权协议和署名要求。 |
常见使用场景
- 在动手写集成代码前,先确认第三方小组件、仪表盘或文档能否嵌入自己的产品。
- 验证自己的站点是否按预期下发了 X-Frame-Options 或 CSP frame-ancestors 策略。
- 排查点击劫持(clickjacking)防护:弄清页面为何在 iframe 中显示空白或拒绝加载。
- 做内容聚合、门户集成时批量筛查候选 URL,并通过会话历史随时回看检测结果。
- 在安全评审或渗透测试前,记录外部依赖的框架嵌套策略作为佐证材料。