iframe 嵌入檢測

免費線上 iframe 檢測工具:輸入任意網頁 URL,檢查其 X-Frame-Options 與 Content-Security-Policy frame-ancestors 回應頭,判斷頁面是否允許被 iframe 巢狀,並透過實機預覽再次確認。檢測歷史僅保留於目前分頁的會話(Session)中。

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 規則返回不同的回應標頭,重新檢測即可查看最新狀態。