---
title: "同一オリジンとは？判定条件と同一オリジンポリシーが止める操作をChromeで実測"
url: "https://www.issoh.co.jp/tech/details/7544/"
published: 2025-07-03
updated: 2026-10-18
categories: ["セキュリティ"]
publisher: "株式会社一創"
---

# 同一オリジンとは？判定条件と同一オリジンポリシーが止める操作をChromeで実測

フロントエンドからAPIを呼んで`TypeError: Failed to fetch`になったとき、あるいは`iframe`の中身をJavaScriptから触ろうとして`SecurityError`が出たとき、まず確かめるのが「同一オリジンかどうか」です。同一オリジンの判定条件は3つしかありませんが、何が止まり何が止まらないかは仕様ごとに散らばっています。この記事では判定条件を確定させたうえで、同一オリジンポリシー（Same-Origin Policy、SOP）の挙動を**Chrome 155で2つのオリジンを立てて実測した結果**とあわせて整理します。

## まとめ

- 同一オリジンとは**スキーム・ホスト・ポートの3つがすべて一致**する状態です。パスやクエリは判定に関係しません。
- `http`と`https`、`example.com`と`www.example.com`、ポート80と8080は、いずれも別オリジンです。
- SOPが止めるのは主に**応答の読み取り**です。実測でも、CORS許可のない`fetch`は`TypeError`で失敗した一方、リクエスト自体はサーバーに届いていました。フォームのPOSTも届きます。だからSOPだけではCSRFを防げません。
- `allow-same-origin`を指定していない`sandbox`付きの`iframe`は不透明なオリジン（`"null"`）になり、localStorageにも触れません。`allow-same-origin`と`allow-scripts`を同一オリジンの文書に同時に与えると、中から`sandbox`属性を外せます（実測で成功）。
- `same-origin`という値はCOOP・CORP・X-Frame-Options・Sec-Fetch-Siteなどに登場し、守る対象がそれぞれ違います。COOPは**安全なコンテキスト（HTTPSやlocalhost）のページでしか処理されず**、Sec-Fetch-Siteは**リクエスト先がHTTPSなど潜在的に信頼できるURLのときだけ**付きます。
- `document.domain`への代入はChrome 115以降、安全なコンテキストでは既定で例外を出さずに無視されます（`Origin-Agent-Cluster: ?0`で戻せる）。非安全なHTTPのページでは実測で代入が効きましたが、この挙動に頼らず`postMessage`へ移行します。

## 同一オリジンとは：スキーム・ホスト・ポートの3点一致

オリジン（origin）とは、URLのうち**スキーム・ホスト・ポート**の3要素を取り出した組です。MDNの[同一オリジンポリシー](https://developer.mozilla.org/ja/docs/Web/Security/Same-origin%5Fpolicy)は「二つのページのプロトコル、ポート番号 (もしあれば)、ホストが等しい場合、両者のページは同じオリジンです」と定義しています。概念を独立した文書として定義したのはIETFの[RFC 6454「The Web Origin Concept」](https://www.rfc-editor.org/rfc/rfc6454.txt)（2011年12月）で、3.2節に「two URIs are part of the same origin if they have the same scheme, host, and port.」とあります。

### 同一オリジンの判定表：スキーム・ホスト・ポートの比較

`http://store.company.com/dir/page.html`を基準にした判定です（MDNの表に、つまずきやすい2行を補いました）。

| 比較対象のURL                                   | 結果     | 理由              |
| ------------------------------------------ | ------ | --------------- |
| http://store.company.com/dir2/other.html   | 同一オリジン | パスだけが異なる        |
| http://store.company.com/dir/page.html?a=1 | 同一オリジン | クエリは判定に関与しない    |
| https://store.company.com/page.html        | 別オリジン  | スキームが異なる        |
| http://store.company.com:81/dir/page.html  | 別オリジン  | ポートが異なる         |
| http://news.company.com/dir/page.html      | 別オリジン  | ホストが異なる         |
| http://company.com/dir/page.html           | 別オリジン  | サブドメインの有無もホスト違い |

既定ポートは省略されていても同じものとして扱われます。`http://example.com`と`http://example.com:80`は同一オリジンです。開発中に`localhost:3000`のフロントから`localhost:8080`のAPIを呼ぶ構成が別オリジンになるのは、ポートの行が理由です。

### オリジンとサイトの違い：ポートとサブドメインを無視する単位

紛らわしいのが**サイト（site）**という単位です。サイトはスキームと登録可能ドメイン（eTLD+1）で判定され、サブドメインとポートの違いを無視します。`https://app.example.com`と`https://api.example.com`は**別オリジンだが同一サイト**です。登録可能ドメインは単純な末尾2ラベルではなく、[URL Standard](https://url.spec.whatwg.org/#host-registrable-domain)がPublic Suffix Listで求めると定めています。たとえば`github.io`はpublic suffixなので、`a.github.io`と`b.github.io`は別サイトです。スキームを判定に含める「schemeful same-site」は、Chrome 89のリリース資料で全利用者への展開が告知されました。[web.devの解説](https://web.dev/articles/schemeful-samesite)のとおり、`http://example.com`と`https://example.com`は別サイトです。

ブラウザはリクエストごとにこの関係を`Sec-Fetch-Site`ヘッダーでサーバーへ伝えます。値は`same-origin`・`same-site`・`cross-site`・`none`（アドレスバー入力などの直接操作）の4つです（[Fetch Metadata](https://www.w3.org/TR/fetch-metadata/#sec-fetch-site-header)）。後述の実測では、`a.example.test`のページからのリクエストに、同じホストへは`same-origin`、`b.example.test`へは`same-site`、`other.test`へは`cross-site`が付きました。ただし仕様は「リクエスト先がpotentially trustworthy URLでなければ付けない」と定めており、素のHTTPで同じ実験をすると、このヘッダーは1件も付きませんでした。

Cookieの`SameSite`属性もサイト単位の判定です。一方、Cookieが実際にどのホストへ送られるかは`Domain`・`Path`・`Secure`属性で決まり、`Domain`を省略したCookieは発行元のホストにだけ返ります。DOMやlocalStorageへのアクセスはオリジン単位なので、同じサイトでもサブドメインが違えば触れません。

### 不透明なオリジンとオリジンの継承：about:blank・srcdoc・data:

URLから素直に3要素を取れない文書もあります。[HTML Standardのオリジン決定手順](https://html.spec.whatwg.org/multipage/document-sequences.html#determining-the-origin)は、`about:blank`と`srcdoc`の文書には作成元のオリジンを**継承**させ、`data:`URLの文書には新しい**不透明なオリジン**（自分自身以外のどのオリジンとも一致しない内部値）を与えます。実測でも、`src`なしの`iframe`と`srcdoc`の`iframe`は親と同じオリジンになり中のDOMを操作できました。`data:text/html,...`を読み込んだ`iframe`は`SecurityError`で触れませんでした。

ほかに不透明なオリジンになる代表例が、`sandbox`属性付きの`iframe`（後述）と、`file:///`で開いたローカルファイルです。MDNは「現行のブラウザーでは、file:/// スキームを使用して読み込まれたファイルのオリジンは、通常、不透明なオリジンとして扱われます」と述べています。HTMLファイルをダブルクリックで開くと`fetch`が失敗し、`http://localhost`経由なら動くのはこのためです。

## 同一オリジンポリシーが止めるもの・止めないもの

SOPは「他オリジンとのやり取りを全部禁止する仕組み」ではありません。MDNはクロスオリジンのアクセスを、**書き込み**（リンク・リダイレクト・フォーム送信）は概して許可、**埋め込み**（画像・スクリプト・iframe）は概して許可、**読み込み**は一般に不許可、の3つに分けています。操作ごとに整理すると次のとおりです。

| クロスオリジンでの操作            | SOPの扱い | 補足             |
| ---------------------- | ------ | -------------- |
| リンク遷移・リダイレクト           | 許可     | 書き込みに分類        |
| フォーム送信（POST含む）         | 許可     | 送信は届く。応答は読めない  |
| img・script・cssの読み込み    | 許可     | 埋め込みに分類        |
| iframeでのページ埋め込み        | 許可     | 中のDOMには触れない    |
| fetch・XHRの応答の読み取り      | 禁止     | CORSで許可すれば可    |
| iframe内・別窓のDOM操作       | 禁止     | postMessageで代替 |
| localStorage・IndexedDB | 禁止     | オリジンごとに領域が別    |

### SOPだけではCSRFを防げない理由

表の2行目が決定的です。攻撃者のページに置かれた`form`が自社サイトへPOSTすること自体は、SOPでは止まりません。実測でも、別オリジンへ送ったフォームの本文`amount=1000`はそのままサーバーに届きました。そのリクエストにCookieが付くかどうかは`SameSite`属性で決まります。MDNは、未指定時に`Lax`を既定とするのは一部のブラウザで、そのブラウザでは発行から2分以内ならトップレベル遷移のクロスサイトPOSTにもCookieを含める互換措置があると注記しています。iframe内への送信やfetchに効く例外ではありません。**CSRFは応答の読み取りを必要とせず、サーバーが攻撃者の意図した処理を実行すると成立する**ので、応答を読めないことは防御になりません。

CSRFにはトークン方式や`SameSite`属性といった別の対策が要ります。詳しくは[CSRFトークンとは？仕組み・Double Submit CookieとSPAでの実装](/column/details/3984/)と[XSSとCSRFの違いとは？仕組み・被害・対策を実装レベルで徹底比較](/tech/details/4109/)で扱っています。同様に、XSSで自サイトに注入されたスクリプトはそのオリジンの正規スクリプトとして動くため、SOPの制限を受けません。ここを塞ぐのは[コンテンツセキュリティポリシー（CSP）とは？nonce方式の設定とXSS防御の実装判断を解説](/tech/details/16358/)で説明しているCSPです。

### クロスオリジンでも触れるWindowのプロパティ

別オリジンの`iframe`や`window.open`で開いた窓の`Window`オブジェクトは、完全に遮断されるわけではありません。[HTML StandardのCrossOriginProperties](https://html.spec.whatwg.org/multipage/nav-history-apis.html#crossoriginproperties-%28-o-%29)が許可しているのは、`window`・`self`・`location`・`close`・`closed`・`focus`・`blur`・`frames`・`length`・`top`・`opener`・`parent`・`postMessage`の13項目です。`Location`は`href`への**書き込み**と`replace()`だけが許可され、読み取りはできません。実測でも、別オリジンの`iframe`に対して`contentWindow.closed`と`length`は読めた一方、`contentWindow.location.href`の読み取りと`contentWindow.document`の参照は`SecurityError`になり、`contentDocument`は`null`でした。

### SOPの仕様上の位置づけ：RFC・HTML・Fetchの分担

SOPを「W3Cが定めた勧告」と説明する解説がありますが、正確ではありません。オリジンの概念はRFC 6454（IETF）が定義し、どの操作をどう制限するかは**WHATWGのHTML StandardとFetch Standardに分散**しています。Cookie・Web Storage・Service Workerなどは各仕様が個別に同一オリジン要件を持ちます。調べるときは、機能ごとにその仕様を当たるのが近道です。

## Chrome 155での実測：3オリジン間のアクセス制限と送信結果

ここまでの線引きを、2026年10月18日にChrome 155.0.8059.82（headless）とpuppeteer-coreで確かめました。Node.jsのサーバー1台に`a.example.test`・`b.example.test`・`other.test`の3つのホスト名を向け、`a.example.test:8001`のページから他の2つへ操作を仕掛けています。サーバーは受け取ったリクエストをすべてログに残すので、「ブラウザで失敗した操作のリクエストがサーバーに届いたか」まで分かります。

```bash
# 以下はChrome起動時に渡す2つの引数の抜粋です。単独のシェルコマンドではありません。
--host-resolver-rules="MAP *.example.test 127.0.0.1, MAP other.test 127.0.0.1"
--unsafely-treat-insecure-origin-as-secure=http://a.example.test:8001,http://b.example.test:8001,http://other.test:8001
```

```javascript
// a.example.test のページで実行。CORSヘッダーの無い b.example.test の JSON を読む
try {
  const r = await fetch("http://b.example.test:8001/data");
  console.log(await r.text());
} catch (e) {
  console.log(e.name, e.message); // => TypeError Failed to fetch
}
// サーバーログには GET /data（Origin: http://a.example.test:8001）が残る
```

| a.example.testからの操作            | ブラウザ側の結果                    | サーバーに届いたか |
| ------------------------------ | --------------------------- | --------- |
| CORSなしのfetch                   | TypeError: Failed to fetch  | 届いた       |
| 同じURLへmode: no-corsでfetch      | type=opaque・status 0・本文0バイト | 届いた       |
| CORS許可ありのfetch                 | 本文を読めた                      | 届いた       |
| 別オリジンへのフォームPOST                | 送信成功                        | 届いた（本文も）  |
| CORP: same-originの画像           | onerror（表示されない）             | 届いた       |
| CORSなしの画像をcanvasへ              | toDataURLでSecurityError     | 届いた       |
| sandbox=”allow-scripts”のiframe | 子のoriginは”null”             | 届いた       |

表の右列がすべて「届いた」になっている点が、この実験でいちばん見てほしいところです。SOPもCORPもcanvasの汚染も、今回の条件では**取得済みの応答や画素データへのアクセスを制限する**ため、リクエストは送信されました。ただし、CORSプリフライトが失敗すれば本リクエストは送られず、fetchのsame-originモードでも別オリジンへの送信は拒否されます。サーバーのアクセスログに正常な200が並んでいても、ブラウザ側ではエラーになっていることがある、ということです。CORSエラーの調査でサーバーログだけを見て「リクエストは成功している」と判断するのは誤りで、開発者ツールのコンソールを合わせて見る必要があります。

検証スクリプトとログは、HTTPのまま実行した場合と安全なコンテキストとして実行した場合の2通りを残しています。HTTPのままでは、後述のCOOPと`Sec-Fetch-Site`が効かず、`document.domain`の代入が通るという差が出ました。手元で再現するときは、どちらの条件で動かしているかを先に確かめてください。

## 同一オリジンが要求される主なAPIと、その代替

実装で詰まりやすいのは「このAPIは同一オリジンが必要か」という個別の判断です。

| API・機能                      | 同一オリジン要件   | クロスオリジンでの代替                 |
| --------------------------- | ---------- | --------------------------- |
| fetch・XMLHttpRequest        | 応答の読み取りに必要 | CORSヘッダーで許可                 |
| iframeのDOM操作                | 必要         | postMessage                 |
| localStorage・sessionStorage | 必要         | 共有しない設計にする                  |
| canvasの画素読み出し               | 必要         | crossorigin属性＋CORS          |
| Service Workerの登録           | 必要         | 自オリジンのパスで配信                 |
| Web Fontsの読み込み              | 不要だがCORS必須 | Access-Control-Allow-Origin |

canvasは症状が分かりにくい例です。MDNの[CORS enabled image](https://developer.mozilla.org/en-US/docs/Web/HTML/How%5Fto/CORS%5Fenabled%5Fimage)によると、CORS承認なしに他オリジンの画像を描いた時点でcanvasは**tainted（汚染）**状態になり、`getImageData()`・`toBlob()`・`toDataURL()`を呼ぶと`SecurityError`になります。画像の表示は成功するので、保存やフィルタ処理だけが落ちます。実測では、画像に`crossOrigin = "anonymous"`を付け、サーバーが`Access-Control-Allow-Origin: *`を返すようにしただけで`toDataURL()`が通りました。

[ServiceWorkerContainer.register()](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register)は、スクリプトURLとスコープが登録元と同一オリジンでなければ`SecurityError`を投げます。別オリジンのCDN URLからの登録は成立しませんが、CDNが自オリジンのパスとして配信する構成なら登録できます。

## iframeのsandboxとallow-same-origin

`iframe`に`allow-same-origin`を含まない`sandbox`属性を付けると、中の文書は不透明なオリジンになります。MDNの[iframe要素](https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Elements/iframe)は`allow-same-origin`について「このトークンが使用されていない場合、リソースは同一オリジンポリシーに常に失敗する特別なオリジンからのものとして扱われます」と説明しています。実測では、同じサーバーから配信した文書でも`sandbox="allow-scripts"`を付けると、子の`self.origin`は`"null"`になり、`localStorage.setItem()`と`document.cookie`の参照がどちらも`SecurityError`になりました。これはJavaScriptからのCookie・ストレージ参照の制限で、HTTPリクエストにCookieが付くかどうかは`SameSite`属性や第三者Cookieの設定で別に決まります。文書本来のオリジンを維持するトークンが`allow-same-origin`です。

### allow-scriptsとの同時指定でsandboxが外れる条件

MDNは「埋め込まれた文書のオリジンが埋め込み先のページと同じである場合、`allow-scripts`と`allow-same-origin`を同時に使用すると、埋め込まれた文書から`sandbox`属性を削除することができるようになるため、絶対に避けるべきです」と警告しています。実測でも、親と同一オリジンの文書に2つのトークンを与えると、子の`window.frameElement.removeAttribute("sandbox")`が成功し、親から見た`sandbox`属性は`null`になりました。次に読み込み直した時点で制限は消えます。

```html
<!-- 外部由来のコンテンツ：スクリプトは許すがオリジンは戻さない -->
<iframe src="https://untrusted.example.com/widget"
        sandbox="allow-scripts allow-popups"></iframe>

<!-- 危険：親と同一オリジンの文書なら sandbox を自分で外せる -->
<iframe src="/embed/editor"
        sandbox="allow-scripts allow-same-origin"></iframe>
```

自己解除が成立するのは、埋め込む文書が親と**同一オリジンのときだけ**です。別オリジンの文書に`allow-same-origin`を与えてもsandboxは外されませんが、その文書自身のCookieやストレージへのアクセスを許すことになります。判断の基準は次のとおりです。

- ユーザー投稿や外部提供のコンテンツは、`allow-scripts`を付けても`allow-same-origin`は付けない。不透明なオリジンのまま動かす。
- 自オリジンの信頼できる文書にスクリプトと同一オリジンの両方が要るなら、sandboxで隔離する意味が薄いので設計を見直す。どうしても隔離したいなら、配信を別オリジン（サンドボックス用の別ドメイン）に移す。

[HTML Standard](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-sandbox)が列挙する`sandbox`の値は、2026年10月18日時点で`allow-same-origin`を含む13種です。MDNの一覧に載る`allow-storage-access-by-user-activation`はこの列挙に無く、Chrome 135から使える`allow-same-site-none-cookies`も標準外の値です（[Privacy Sandboxの告知](https://privacysandbox.google.com/blog/sandbox-allow-same-site-none-cookies)）。各値の役割とCookie分割の対処は[iframeのsandbox属性とCookie分割の解説](/tech/details/17529/)にまとめています。

## same-originという値をとるヘッダー・属性の早見表

`same-origin`という文字列は、SOPとは別に複数のヘッダーの設定値として使われます。名前は似ていますが守る対象が違います。

| ヘッダー・属性                      | 値                                      | 守る対象         |
| ---------------------------- | -------------------------------------- | ------------ |
| iframeのsandbox               | allow-same-origin                      | 埋め込んだ文書のオリジン |
| Cross-Origin-Opener-Policy   | same-origin / same-origin-allow-popups | 開いた窓との参照     |
| Cross-Origin-Resource-Policy | same-origin                            | 他オリジンからの読み込み |
| X-Frame-Options              | SAMEORIGIN                             | 自ページの被埋め込み   |
| Sec-Fetch-Site（リクエスト）        | same-origin                            | ブラウザが付ける判定結果 |
| Referrer-Policy              | strict-origin-when-cross-origin        | Refererの送信範囲 |

### COOPによるポップアップとの参照分離

[Cross-Origin-Opener-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy)（COOP）は、自分が開いた窓や自分を開いた窓と参照を共有するかを決めます。既定値は`unsafe-none`です。実測では、`same-origin`を返すページから別オリジンを`window.open`すると、1.5秒後の`w.closed`が`true`になり参照が切れました。`unsafe-none`と`same-origin-allow-popups`では`false`のままです。OAuthの認可画面や決済画面をポップアップで開く構成は`same-origin`だと壊れるので、MDNは`same-origin-allow-popups`の用途を「when using a cross-origin service for OAuth or payments」と明記しています。

注意点として、HTML Standardの手順は非安全なコンテキストではCOOPを読まずに既定値を返します。実測でもHTTPのままでは3つの値すべてで`false`になり、差が出ませんでした。`SharedArrayBuffer`などに要るクロスオリジン分離は、COOPを`same-origin`にしたうえでCOEPを`require-corp`（または`credentialless`）にして初めて成立します。

### CORPとX-Frame-Options：読み込み制御と埋め込み制御

[Cross-Origin-Resource-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Resource-Policy)（CORP）は`same-site`・`same-origin`・`cross-origin`の3値で、`same-origin`は他オリジンからのno-corsモードの読み込みを拒否します。CORSモードで取得し、サーバーがCORSを許可する場合は利用できます。実測の表のとおり画像はエラーになりますが、リクエストは届いています。直リンクによる帯域消費への対策にはなりません。

[X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options)の`SAMEORIGIN`は逆方向で、自分のページが他オリジンのフレームに埋め込まれるのを防ぎます。MDNの定義は「The document can only be embedded if all ancestor frames have the same origin as the page itself.」で、直近の親だけでなく**すべての祖先フレーム**が対象です。実測では、同じオリジンからは表示され、`b.example.test`のページに埋め込むとフレームが`chrome-error://chromewebdata/`に置き換わりました。`ALLOW-FROM`は廃止済みなので、特定サイトにだけ許すならCSPの`frame-ancestors`を使います。設定と検証手順は[クリックジャッキングとは？frame-ancestorsで防ぐ実装と検証手順を解説](/tech/details/16360/)にあります。

### strict-origin-when-cross-origin：オリジン別のReferer送信範囲

`Referrer-Policy`の`strict-origin-when-cross-origin`は、ヘッダーを指定しないときの既定値です。MDNの互換性データでは、既定になったのはChrome 85・Firefox 87・Safari 15です。[Referrer Policyの仕様](https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin-when-cross-origin)によると、同一オリジンへのリクエストにはパスとクエリを含むURLを、クロスオリジンへのリクエストにはオリジンだけを送り、HTTPSからHTTPへ下がるときは送りません。

実測でも、`http://a.example.test:8001/page?user=42`のページから同じオリジンへのfetchには`Referer: http://a.example.test:8001/page?user=42`が付き、`b.example.test`（同一サイト・別オリジン）へは`Referer: http://a.example.test:8001/`だけが付きました。判定はサイトではなく**オリジン**単位なので、サブドメイン間の遷移でもパスとクエリは渡りません。サブドメインをまたぐアクセス解析でRefererのパスが空になるのはこのためです。Refererの値を使った制御やアクセス解析への影響は[リファラー（Referer / Referrer）とは？仕組み・リファラー制御と「リファラを有効にしてください」の対処](/column/details/6995/)で扱っています。

## クロスオリジンで正しくやり取りする方法

SOPを緩めるのではなく、必要な通信だけを明示的に許可するのが現在の作法です。

| 手段          | 向いている場面       | 評価           |
| ----------- | ------------- | ------------ |
| CORS        | APIの応答を読みたい   | 標準。第一候補      |
| postMessage | iframe・別窓との連携 | 標準。オリジン検証が必須 |
| リバースプロキシ    | 同一オリジンに寄せたい   | 設定次第で有効      |
| JSONP       | 過去の手法         | 新規採用しない      |

CORSはサーバーが`Access-Control-Allow-Origin`などで許可を宣言する仕組みで、プリフライトやエラーの読み方は[CORSとは？仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説](/tech/details/13763/)で詳しく扱っています。JSONPは`script`タグが読み込み制限の対象外である性質を使った古い手法で、提供元に任意のスクリプト実行を許すため採用しません（[JSONPとは？読み方・仕組みとCORSとの違い、fetchで使えない理由とXSS対策](/tech/details/8182/)）。

### postMessageの検証条件：オリジン・送信元・データ形式

送信時は宛先オリジンを指定し、受信時は`event.origin`を必ず確かめます。省くと任意のサイトから送られたメッセージを処理してしまいます。

```javascript
// 送信側：宛先オリジンを明示する（"*" は使わない）
iframe.contentWindow.postMessage(
  { type: "updateUser", id: 42 },
  "https://app.example.com"
);

// 受信側：オリジン・送信元・データ形式を検証してから処理する
window.addEventListener("message", (event) => {
  if (event.origin !== "https://parent.example.com") return;
  if (event.source !== window.parent) return;
  if (event.data?.type !== "updateUser" || !Number.isInteger(event.data.id)) return;
  handle(event.data);
});
```

### document.domainの制限：Chromeの既定動作と例外

かつては`document.domain`に上位ドメインを代入し、サブドメイン間でオリジンを揃える方法がありました。Chrome for Developersの[document.domain setter deprecation](https://developer.chrome.com/blog/document-domain-setter-deprecation)は「Starting in Chrome 115, websites will be unable to set document.domain」と告知し、継続が必要な場合の最終手段として`Origin-Agent-Cluster: ?0`レスポンスヘッダーを挙げています。HTML Standardの`document.domain`の手順も、オリジン単位のエージェントクラスタに属する文書では何もせずに戻ると定めています。

自己署名証明書のHTTPSで確かめると、Chrome 155では`a.example.test`のページで`document.domain = "example.test"`を実行しても例外は出ず、値は`a.example.test`のままで、`b.example.test`の`iframe`には`SecurityError`で触れませんでした。`Origin-Agent-Cluster: ?0`を両方の文書に付けると代入が効き、中のDOMを操作できました。ここまでは告知どおりです。

見落とされやすいのは**HTTPのページ**です。同じ実験を素のHTTPで行うと、ヘッダーなしでも代入が効き、`iframe`の中に触れました。このとき`window.originAgentCluster`は`false`でした。HTML Standardも、非安全なコンテキストでは`Origin-Agent-Cluster`ヘッダーによる要求を無効にすると定めています。社内システムなどHTTPのままのサイトでは古い`document.domain`のコードが動き続け、HTTPSへ移行した日に突然サブドメイン間の連携が壊れる、という順番で問題が表に出ます。HTTPS化の前に`postMessage`へ置き換えておくのが安全です。なお、Firefoxのmainブランチのソースでは既定でのsetter無効化の設定値は`false`で、ブラウザによって挙動が違う点にも注意が要ります。

## SOP関連エラーの症状別原因と切り分け手順

コンソールに出るエラーは、原因ごとに文面が違います。

| 症状                                 | 疑うべき原因                            |
| ---------------------------------- | --------------------------------- |
| Access-Control-Allow-Originがない旨の表示 | サーバーのCORS設定                       |
| iframeのDOM参照でSecurityError         | クロスオリジンまたはsandbox                 |
| canvasのtoDataURLでSecurityError     | 画像のCORS未設定                        |
| フレーム表示が拒否された旨の表示                   | X-Frame-Optionsかframe-ancestors   |
| Service Workerの登録でSecurityError    | スクリプトが別オリジン                       |
| localhostへのfetchで許可の確認が出る          | Local Network Access（Chrome 142〜） |

切り分けは、まず開発者ツールのネットワークタブでリクエストが**実際に送信されているか**を見ます。送信されて応答も返り、コンソールにCORS違反が出ているなら、読み取りだけが止められた状態です。リクエストが飛んでいないなら、プリフライトの失敗かCSPなど別の仕組みを疑います。

最後の行は2025年以降に増えた原因です。[Chrome 142のリリースノート](https://developer.chrome.com/release-notes/142?hl=en)は、公開サイトからローカルネットワークやループバック（localhost）へのリクエストを許可プロンプトで制御するLocal Network Accessを導入したと記しています。利用者が許可しても、応答をJavaScriptで読むには通常どおりCORSの許可が要ります。プロンプトの拒否とCORS違反は別の問題として確かめます。

## よくある質問

### 同一オリジンとはどういう意味ですか？

2つのURLのスキーム（`http`か`https`か）・ホスト・ポート番号の3つがすべて一致している状態です。パス・クエリ・フラグメントは判定に影響しません。`example.com`と`www.example.com`は別オリジンで、既定ポート（httpは80、httpsは443）は省略されていても同じとみなされます。

### 同一オリジンポリシーの目的は何ですか？

あるオリジンのスクリプトが、別オリジンのリソースの中身を読み取れないようにすることです。これがないと、開いた悪意あるページのスクリプトが、同じブラウザで開いているWebメールやネットバンキングの内容を読めてしまいます。ただし送信と埋め込みは許可されているため、SOPの中心は読み取りの遮断です。

### 同一生成元ポリシーとは？

Same-Origin Policyの訳語のひとつで、同一オリジンポリシーと同じものです。「同一オリジンポリシー」「同一生成元ポリシー」「SOP」は意味の違いなく併用されています。Wikipedia日本語版が「同一生成元ポリシー」を項目名にしているため、この表記もよく見かけます。

### 「Config URL must be on same origin.」と表示されるのはなぜですか？

360度パノラマビューアのPannellumが出すエラーです。ブラウザの標準メッセージではありません。`pannellum.htm?config=...`の形で設定JSONを渡すスタンドアロン表示で、設定ファイルのURLのオリジンが表示ページと違うと、この文言を出して処理を止めます。[ソースコード](https://github.com/mpetroff/pannellum/blob/master/src/standalone/standalone.js)には「Prohibit cross-origin requests, to mitigate XSS risk」というコメントがあり、この判定は[2026年2月のコミット](https://github.com/mpetroff/pannellum/commit/b28b339b57e6625698bbea8b4e7fcf6da4f558c1)で入り、2.5.6には無く2.5.7から含まれています。設定JSONを`pannellum.htm`と同じオリジンに置けば解消します。

### ローカルのHTMLファイルでfetchが失敗するのはなぜですか？

`file:///`で開いたファイルのオリジンが、不透明なオリジンとして扱われるためです。同じフォルダのファイル同士でも同一オリジンとはみなされず、CORSエラーになることがあります（file URLの扱いは実装依存です）。`python3 -m http.server`などで簡易サーバーを立て、既定ポートの`http://localhost:8000`からHTMLと取得対象を配信します。別オリジンのAPIを呼ぶ場合は、別途CORSの許可が必要です。

## 関連記事

- [CORSとは？仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説](/tech/details/13763/)
- [コンテンツセキュリティポリシー（CSP）とは？nonce方式の設定とXSS防御の実装判断を解説](/tech/details/16358/)
- [クリックジャッキングとは？frame-ancestorsで防ぐ実装と検証手順を解説](/tech/details/16360/)
- [CSRFトークンとは？仕組み・Double Submit CookieとSPAでの実装](/column/details/3984/)
- [XSSとCSRFの違いとは？仕組み・被害・対策を実装レベルで徹底比較](/tech/details/4109/)

---

出典: [同一オリジンとは？判定条件と同一オリジンポリシーが止める操作をChromeで実測](<https://www.issoh.co.jp/tech/details/7544/>)（株式会社一創）
