iframeは廃止されたタグではありません。MDNのiframe要素のリファレンスは、2026年9月時点でallowやsandboxを含む15種類の属性を現役として列挙し、非推奨に分類しているのはframeborderなど6種類だけです。実装で詰まるのはタグの書き方ではありません。sandboxのトークンをどこまで開けるか、埋め込まれる側のヘッダーで拒否されていないか、iframe内でCookieが送られずセッションが切れる状態をどう回避するか、という3点に集約されます。この記事では属性の全体像、sandboxで避けるべき組み合わせ、frame-ancestorsによる拒否設定、Cookie分割への対応、postMessageでのorigin検証、そしてiframeを選ばない場面までを公式仕様に沿って整理しました。
まとめ:iframe実装で先に固める4つの設定と採用判断
外部サービスを埋め込むなら、書くべき属性は決まっています。sandboxで権限を絞り、allowでカメラや位置情報を止め、referrerpolicyでリファラを限定し、loadingで遅延読み込みにする。この4つを最初から書き、必要になった機能だけを開け直す順序が安全側です。
設計上の分かれ目は2つです。1つ目は、埋め込む相手のドメインが自社と異なる場合にCookieが送られるかどうか。サードパーティCookieが遮断された環境では、Set-CookieにSameSite=NoneとPartitionedを併記するか、Storage Access APIで例外的に許可を得るかの二択です。2つ目は、そもそもiframeで埋めるべきかどうか。決済画面や外部SaaSのウィジェットのように、相手側のオリジンで動くこと自体に意味がある対象には向きます。自社UIの見た目を分離したいだけならShadow DOMによるカプセル化のほうが素直で、検索エンジンに評価させたい本文をiframeに入れる選択は避けてください。オリジンをまたぐ通信の前提は同一オリジンの判定条件で整理しています。
iframeが別ブラウジングコンテキストを作る仕組みと主要属性の役割
iframeは、親の文書の中に入れ子のブラウジングコンテキストを作る要素です。HTML Standardのiframe / embed / object の節が、この入れ子コンテキストとsandboxのフラグ集合を定義しています。中の文書は独立した閲覧単位として扱われ、親のCSSやJavaScriptから直接触れません。この隔離が利点でもあり、高さの自動調整やスタイル共有ができない制約の出どころでもあります。
srcとsrcdocの使い分けと表示サイズをCSSで決める書き方
読み込み元の指定はsrcとsrcdocの2通りです。srcはURLを指し、srcdocはHTMLの断片を属性値として直接埋め込みます。両方を書いた場合はsrcdocが優先されました。srcdocは外部リクエストを発生させずに独立した文脈を作れるため、ユーザー投稿のHTMLをプレビュー表示する用途に向きます。
幅と高さはwidth属性とheight属性でも指定できますが、値はCSSピクセル固定です。レスポンシブにするなら、属性値は初期のレイアウトずれを防ぐ目安として残し、実寸はCSS側でwidthを100%、高さをaspect-ratioやvh単位で決めます。title属性の指定も忘れないでください。支援技術がフレームの中身を説明する唯一の手がかりです。
非推奨属性6種類の置き換え先とscrolling除去時の確認点
MDNのiframe要素リファレンスが非推奨に分類しているのは、align・frameborder・longdesc・marginheight・marginwidth・scrollingの6種類です。既存サイトの改修で残っているのはframeborder=”0″とscrolling=”no”でしょう。前者はCSSのborder: none、後者は埋め込まれる側の文書のoverflowで置き換えます。ただしscrollingを外すと、中身が想定より高いときに内容が切れます。狭い画面幅での見え方を実機で確認してから外してください。
sandbox属性で権限を落とす設定手順と避けるべき組み合わせ
sandboxは、iframeの中でできることを既定で禁止し、必要な機能だけをトークンで開け直す属性です。埋め込む相手が自社の管理下にないなら、sandboxを書かない選択は、相手のJavaScriptに親ページの遷移とフォーム送信、ポップアップの開閉を任せるのと同義です。
sandbox無指定・空文字・トークン付与で変わる制限の差分
挙動は3段階に分かれます。属性を書かない場合は制限なし。sandbox=””のように空文字で書くと最も厳しい制限がかかり、スクリプトもフォームも動きません。トークンを空白区切りで並べると、その機能だけが解除されます。MDNが列挙するトークンは次の14種類です。
- allow-downloads:ダウンロード
- allow-forms:フォーム送信
- allow-modals:モーダル表示
- allow-orientation-lock:画面の向きのロック
- allow-pointer-lock:ポインターロック
- allow-popups:ポップアップ表示
- allow-popups-to-escape-sandbox:開いた窓へ制限を継承しない
- allow-presentation:プレゼン開始
- allow-same-origin:元オリジンとしての扱い
- allow-scripts:スクリプト実行
- allow-storage-access-by-user-activation:ストレージアクセス要求
- allow-top-navigation:最上位の遷移
- allow-top-navigation-by-user-activation:操作起因の遷移のみ
- allow-top-navigation-to-custom-protocols:独自スキームへの遷移
運用上は、外部フォームならallow-scriptsとallow-formsの2つ、別窓を開く決済ならallow-popupsを足す程度で足ります。allow-top-navigationが必要になるのは、埋め込み側が親ページを別URLへ飛ばす仕様のときだけです。
allow-scriptsとallow-same-originの同時指定を避ける理由
MDNは、埋め込まれた文書のオリジンが親と同じ場合にallow-scriptsとallow-same-originを同時指定することを「絶対に避けるべき」とし、その状態では埋め込まれた文書側からsandbox属性を削除できるため「sandbox属性をまったく使用しないよりも安全性が低下します」と書いています。JavaScriptが動き、かつ同一オリジンとして扱われるなら、子は親のDOMに触れてiframeタグ自体を書き換えられるという理屈です。次の指定はスクリプトとフォーム送信、別窓の表示だけを開け、カメラと位置情報はallow属性で落とした形です。
<iframe
src="https://widget.example.com/form"
sandbox="allow-scripts allow-forms allow-popups"
allow="camera 'none'; geolocation 'none'"
referrerpolicy="strict-origin-when-cross-origin"
loading="lazy"
title="お問い合わせフォーム"
width="640" height="820"></iframe>
コード例のallow属性は、カメラ・位置情報などの使用可否をiframe単位で決める指定です。allow="camera 'none'; geolocation 'self'"のように機能名と許可元の組を書きます。同じ許可リストをレスポンスヘッダー側で配るのがPermissions-Policyヘッダーで、親でヘッダーが禁止した機能を子のallow属性で開け直すことはできません。自社の別サブドメインを埋め込む場合にallow-same-originを付けたくなったら、Cookieの共有だけが目的でないかをDomain属性の設計に戻って確認してください。
埋め込まれる側でフレーム化を拒否するヘッダーの設定と現行の推奨
ここまでは埋め込む側の設定でした。自社ページを他サイトのiframeに入れられたくない場合は、埋め込まれる側がレスポンスヘッダーで拒否します。設定を怠ったページは、透明化したフレームに重ねて操作を誘導する攻撃の的です。成立条件と検証手順はクリックジャッキングの実装と検証で扱っています。
X-Frame-OptionsのALLOW-FROMが無視される現状と代替
X-Frame-Optionsで有効な値は、どこからのフレーム化も拒否するDENYと、同一オリジンの祖先だけを許すSAMEORIGINの2つです。特定のパートナードメインだけ許可する用途で使われていたALLOW-FROMについて、MDNのX-Frame-Optionsのページは「古いディレクティブ」と位置づけ、「現行のブラウザーは、そのヘッダーを完全に無視します」と警告しています。書いても許可にも拒否にもならず、ヘッダー全体が無視されて誰でも埋め込める状態が残ります。許可元をドメイン単位で指定するなら、移行先はCSPのframe-ancestorsだけです。
frame-ancestorsで許可元を列挙するヘッダーの設定例
frame-ancestorsディレクティブは、ソース値としてhost-source・scheme-source・’self’を受け取ります。’none’を指定した場合の挙動はX-Frame-Options: denyに近いものでした。旧ブラウザ向けにX-Frame-Optionsを併記する構成が現実的な落としどころで、nginxなら次のように書きます。
add_header Content-Security-Policy "frame-ancestors 'self' https://partner.example.com" always;
add_header X-Frame-Options "SAMEORIGIN" always;
設計で外しやすい点が2つあります。1つは配信方法で、frame-ancestorsとsandboxはmeta要素では配信できません。CSPの仕様書が3.3節でその旨を明記しており、HTTPレスポンスヘッダーで返してください。もう1つは入れ子で、MDNは末端フレームのframe-ancestorsですべての祖先を許可する必要があると説明しています。中間のドメインを列挙しないと読み込みが取り消されます。CSP全体の書き方はnonce方式を含むCSPの設定を参照してください。
iframe内でセッションが切れる原因とCookie分割への対応手順
ローカルでは動いたのに本番のiframeに入れるとログイン状態が維持されない、という症状は実装のバグではなくCookieの扱いが原因のことが大半です。親ページと別サイトのiframeという構成では、iframe側のCookieはサードパーティCookieとして扱われ、明示的な指定なしに送られる前提はすでに崩れました。廃止の経緯と全体像はサードパーティCookie廃止の現在地で整理しています。
SameSite=NoneとPartitioned属性を併記する設定の要件
対応の主軸はCHIPS(Cookies Having Independent Partitioned State)です。Privacy SandboxのCHIPSのドキュメントは、Partitioned付きCookieにSecureが必須であること、__Host-接頭辞の使用が推奨されること、サードパーティ文脈で送るにはSameSite=Noneを付けることを示しています。パーティションキーはスキームと登録可能ドメインの組で、埋め込み先ごとに保存領域が分かれます。
Set-Cookie: __Host-sid=8f3c9ab1; Path=/; Secure; HttpOnly;
SameSite=None; Partitioned
対応状況は同ドキュメントの表でChromeとEdgeが114以上、Firefoxが141以上、Safariが26.2以上です。上限は「maximum 180 cookies per partition that cannot exceed 10 KB per-embedded-site」と書かれており、1パーティション180個・埋め込みサイトあたり10KBが天井になります。A/Bテストの割り当てや同意管理の状態をCookieへ逐次書き足している実装では、この10KBが現実的な壁です。セッション識別子だけをCookieに残し、他の状態はサーバー側かIndexedDBへ寄せてください。分割された領域は埋め込み先ごとに独立するため、同じウィジェットをA社サイトとB社サイトに置いても片方のログインは他方へ引き継がれません。
Storage Access APIで例外的に許可を得る3属性の条件
埋め込み先をまたいでログイン状態を共有したい場合の逃げ道がStorage Access APIです。document.requestStorageAccess()を呼ぶと、分割されていないCookieへのアクセス許可をユーザーに求める形になります。
MDNが挙げる条件のうち、実装で見落としやすいものは3つあります。第1に、初回の要求にはタップやクリックに紐づくtransient activationが必須で、読み込み直後に自動で呼ぶことはできません。第2に、sandboxの中から呼ぶならallow-storage-access-by-user-activation・allow-scripts・allow-same-originの3トークンが要ります。第3に、secure contextでなければ動きません。付与の有効期間はブラウザごとに異なり、Firefoxは30日で失効します。要求ボタンは常設し、失効時に再要求できる導線を用意してください。
postMessageによる親子間の通信と読み込み遅延化の実装手順
iframeの中と外は別のブラウジングコンテキストで、直接DOMを触る連携は成立しません。オリジンが異なる場合の受け渡しはpostMessageに限られます。
postMessageのtargetOrigin指定と受信側でのorigin検証
MDNのpostMessage()のリファレンスは、第2引数のtargetOriginで送信先を限定し、受信側でもevent.originを検証するよう求めています。targetOriginに”*”を書くと、遷移後の想定外のオリジンにメッセージが届く余地が生まれます。高さの自動調整を実装する場合の最小形は次のとおりです。
// 子(iframe)側: 実測した高さを親へ通知する
var h = document.documentElement.scrollHeight;
parent.postMessage({ type: "resize", height: h }, "https://www.example.com");
// 親側: origin と type を検証してから反映する
window.addEventListener("message", function (e) {
if (e.origin !== "https://widget.example.com") return;
if (!e.data || e.data.type !== "resize") return;
document.getElementById("w").style.height = e.data.height + "px";
});
受信側の検証を省いた実装は、任意のサイトから同じ形のメッセージを送り込める状態になります。高さの反映だけなら被害は軽いものの、同じ経路でトークンや操作命令を渡していれば侵入口です。originの完全一致で弾き、想定するtypeの値だけを通してください。
loading=lazyで削れる転送量の実測値と適用を外す条件
iframeにはloading=”lazy”が使えます。web.devの計測記事は、データ量が中央値で2〜3%削減、First Contentful Paintが中央値で1〜2%削減と報告しています。個別の事例では、Chrome.comのYouTube埋め込みで約500KBの削減とTTIの10秒短縮、Spotifyで514KB、Facebookのいいねプラグインで215KB(うちJavaScriptが197KB)の削減が示されました。
外すべきケースもあります。初期表示の視野内に入るiframeへlazyを付けると読み込み開始が遅れ、表示までの体感が悪化します。ファーストビューに常時見えるチャットウィジェットや地図は対象外です。COEPを有効にした文書に第三者のiframeを埋める構成ではChromeのiframe credentiallessが使えます。Chrome 110から既定で有効になり、Cookieを持たない一時的なコンテキストとして読み込まれます。ただしMozillaはPending、WebKitはNo signalの段階で、単独の前提には置けません。
iframeを採用してよい条件と別手段へ切り替える判断の分岐点
iframeは「古い技術だから使わない」という判断も「手軽だから使う」という判断も外します。判断軸は1つで、相手のコードを相手のオリジンで走らせる必要があるかどうかです。
決済・外部SaaSの埋め込みでiframeを選ぶ具体的な条件
iframeが最も効くのは、カード番号の入力欄を決済事業者のオリジンに置き、自社サーバーにカード情報を通過させない構成です。入力欄が自社ドメインにあれば自社が保護対象を抱えますが、iframe内なら入力値は決済事業者側の文脈に留まり、PCI DSSの対象範囲を絞れます。
同じ理屈がフォームSaaS・予約ウィジェット・地図・動画にも当てはまります。相手が仕様を変えても埋め込みURLが同じなら追随が不要という運用面の利点も付きました。基幹システムの画面をポータルへ段階的に載せ替える移行期にも有効で、旧画面をiframeで包んで新UIから開き、置き換えの済んだ画面だけ外す進め方が取れます。埋め込みの先で認証連携やデータ同期まで必要になるなら、フレームの中で解こうとせずサーバー間の連携に寄せてください。この切り分けはAPI開発・システム連携で相談を受けている領域です。
iframeを避けるべき3つの場面と代わりに採用する実装手段
採用しないほうがよい場面を条件付きで挙げます。次の3つに当てはまるなら、iframeは選びません。
| 避ける場面 | 代わりに採る手段 |
|---|---|
| 自社UIの見た目の分離 | Shadow DOMでのスコープ化 |
| 本文を検索評価させたい | サーバー側でHTMLに統合 |
| スマホ主体の縦長画面 | 画面遷移かAPI連携で実装 |
1つ目は、自社のコンポーネントのCSS衝突を防ぐ目的でiframeを使う設計です。オリジンが同じならJavaScriptの隔離という利点は得られず、高さ調整とフォーカス管理の負債だけが残ります。2つ目は、iframe内に記事本文や商品説明を置く構成です。3つ目は、スマートフォンで縦に長い画面をiframeに入れる実装で、内側と外側のスクロールが二重になり操作性が崩れます。
失敗パターンとして頻出するのは、動かすためにsandboxへallow-scriptsとallow-same-originを両方書く対処です。詰まった時点で権限を開けるのではなく、なぜ必要になるのかを埋め込み先の仕様に戻って確認してください。
よくある質問
iframeの実装で問い合わせの多い論点を、仕様の記述に沿って答えます。
iframeは今も非推奨なのですか?
要素そのものは非推奨ではありません。MDNのiframe要素リファレンスは2026年9月時点で15種類の属性を現役として掲載し、非推奨扱いはalign・frameborder・longdesc・marginheight・marginwidth・scrollingの6属性だけです。「非推奨」という言い方は、HTML4時代の悪用の経緯と個別属性の非推奨化が混ざって広まったものと考えられます。現在の論点はsandboxとヘッダーの設定にあります。
iframeの中の内容は検索エンジンに評価されますか?
親ページのコンテンツとして扱われる保証はありません。iframeの中身は別のURLを持つ独立した文書で、クロールも評価もその文書単位です。評価させたい本文をiframeに入れる設計は避け、サーバー側でHTMLに統合するか、データをAPIで取得して自社ページ内に描画してください。
iframeの高さを内容に合わせて自動調整できますか?
同一オリジンならJavaScriptで子文書のscrollHeightを読んで反映できます。オリジンが異なる場合は直接読めないため、子側から親へpostMessageで高さを通知する実装が必要です。この方式は埋め込み先の協力が前提で、対応していない外部SaaSではaspect-ratioで比率を固定するか、内側のスクロールを許容する設計へ切り替えます。
sandboxを付けたらフォーム送信もできなくなりますか?
sandbox=””のようにトークンなしで書いた場合はフォーム送信も止まります。送信を許可するにはallow-formsを明記してください。外部フォームの埋め込みなら、allow-scriptsとallow-formsの2つで多くの実装が動きます。動かないときはコンソールに不足しているフラグが出るので、そのメッセージが指すトークンだけを追加し、allow-same-originは足さない方針を守ってください。
iframeが表示されない・拒否されるときはどこを見ればよいですか?
最初に見るのは埋め込み先のレスポンスヘッダーです。X-Frame-OptionsがDENYやSAMEORIGINを返している、あるいはframe-ancestorsに自社ドメインが無い場合、拒否はブラウザ側の正しい動作で埋め込む側では解決できません。入れ子構成なら中間ドメインの許可も確認します。ヘッダーに問題がなく中身だけ動かないなら、sandboxのトークン不足かCookieの未送信を順に切り分けてください。
関連記事
- 同一オリジンとは?判定条件とSame-Origin Policyが止めるもの・止めないもの:iframeの隔離の前提になるオリジン判定を扱います
- クリックジャッキングとは?frame-ancestorsで防ぐ実装と検証手順を解説:フレーム化を拒否しなかった場合の攻撃条件です
- コンテンツセキュリティポリシー(CSP)とは?nonce方式の設定とXSS防御の実装判断を解説:CSP全体の設計をまとめています
- CORSとは?仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説:サーバー間のオリジン制御という別の層です
- Cookie・localStorage・IndexedDBの違いと使い分け:Cookieの上限に当たった時の保存先の選び直しに使えます