iframe 嵌入检测

免费在线 iframe 检测工具:输入任意网页 URL,检查其 X-Frame-Options 与 Content-Security-Policy frame-ancestors 响应头,判断页面是否允许被 iframe 嵌套,并通过实机预览二次确认。检测历史仅保存在当前标签页会话内。

iframe 嵌入检测

:

逐行执行相同的响应头检测,并写入下方的会话历史。每批最多 20 个 URL,会分批并发检测,避免给目标站点造成压力。

已完成: /
URL 结果 HTTP

实机预览

响应头检测是快速初筛,无法识别 JavaScript frame-busting(框架破解脚本):若响应头显示「可嵌入」但预览空白、自动跳出或显示报错页,说明实际无法安全嵌入,以实机预览为准。

检测历史(当前会话)

仅保存在当前标签页(sessionStorage),关闭即清除,不上传服务器。

当前会话还没有检测记录。

检测原理

  1. 输入目标 URL 并点击检测,服务端以真实 iframe 请求(携带 Sec-Fetch-Dest: iframe)获取该页面的响应头信息。
  2. 依据 X-Frame-Options(DENY / SAMEORIGIN 会拦截)与 Content-Security-Policy frame-ancestors(仅 * 或纯协议通配才允许任意第三方父页面)给出结论。
  3. 同一 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,并通过会话历史随时回看检测结果。
  • 在安全评审或渗透测试前,记录外部依赖的框架嵌套策略作为佐证材料。

常见问题

为什么提示「可嵌入」,预览里却是空白?
响应头允许嵌入,但页面很可能含有 JavaScript frame-busting 脚本、需要登录态/Cookie,或根据访问者返回不同内容,以预览结果为准。
可以绕过 X-Frame-Options 或 CSP 吗?
不能。这些响应头由浏览器出于安全(点击劫持防护)强制执行,客户端绕行手段均不可靠。要嵌入不受你控制的内容,应联系站点方在 frame-ancestors 中放行你的来源,或使用对方提供的官方嵌入/API 方案。
DENY 和 SAMEORIGIN 有什么区别?
DENY 禁止页面出现在任何 iframe 中(包括同源页面);SAMEORIGIN 仅允许协议、主机、端口完全一致的同源页面嵌入。
我输入的 URL 会被保存吗?
历史只通过 sessionStorage 存在你自己的浏览器标签页里,关闭标签页即消失。服务端仅在检测时抓取一次目标 URL 以读取响应头。
为什么同一个 URL 不同时间结果不一样?
站点可能按路径、重定向结果、User-Agent、A/B 实验或 WAF 规则下发不同响应头,重新检测即可获得当前状态。