CORSは別オリジンのコードへ応答を渡す許可

CORSはCross-Origin Resource Sharingの略です。あるオリジンのページで動くJavaScriptが別オリジンのサーバーを呼ぶとき、その応答をコードへ渡してよいか、サーバーの許可を使ってブラウザが判断する仕組みです。

たとえば https://app.example の画面が https://api.example の商品一覧を取得する構成を考えます。APIが画面のオリジンを許可するヘッダーを応答に付けると、ブラウザは許可を確認して商品データを画面のコードへ渡します。許可しない場合、リクエストが届いてもコードは応答を読めないことがあります。

参照:出典 s1

Originはブラウザが示し、許可はAPIが返す

Originは呼び出し元のオリジンを示すリクエストヘッダーです。通常のブラウザのJavaScriptが任意の値へ自由に書き換えるヘッダーではありません。APIはこの値を許可リストと比べ、許可する場合にAccess-Control-Allow-Originを応答へ付けます。

次の例は公開の商品一覧です。許可値はURLのパスを含まないオリジンで、一つの値です。複数の候補をカンマで並べる設定ではありません。呼び出し元ごとに許可値を変え、応答を共有キャッシュへ保存するなら、Origin別に応答を区別するVary: Originも必要になります。

説明用の通信例・設定例・擬似コード

GET /products HTTP/1.1
Host: api.example
Origin: https://app.example

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example
Vary: Origin
Content-Type: application/json

[{"name":"Notebook"}]

参照:出典 s1 / 出典 s2

プリフライトは本来のリクエストを送る前の確認

別オリジンへのPUTやDELETEでは、ブラウザは通常、先にOPTIONSで許可を確認します。Content-Typeがapplication/jsonの場合や、Authorizationヘッダーを付ける場合も、この事前確認の対象です。この事前確認をプリフライトと呼びます。既に有効な確認結果がキャッシュされていれば、省略される場合があります。

ブラウザは予定しているメソッドをAccess-Control-Request-Method、対象ヘッダー名をAccess-Control-Request-Headersで伝えます。APIがAccess-Control-Allow-MethodsとAccess-Control-Allow-Headersなどで許可すると、ブラウザは本来のリクエストを送ります。本来のレスポンスにも必要なCORS許可が必要です。

通常のプリフライト自体にはCookieなどの資格情報を付けません。APIがOPTIONSにもログインを必須にすると、正規の画面から本来の通信を始められなくなることがあります。OPTIONSの許可は、実際の更新処理の認証・認可を省いてよいという意味ではありません。

説明用の通信例・設定例・擬似コード

OPTIONS /profile HTTP/1.1
Host: api.example
Origin: https://app.example
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: content-type

参照:出典 s1 / 出典 s2

フォーム相当の送信は事前確認なしで届き得る

GET・HEAD・POSTで、ヘッダーや本文形式などが一定の条件を満たす場合、ブラウザはプリフライトをせずに本来のリクエストを送ります。たとえば通常のフォームに近いapplication/x-www-form-urlencodedのPOSTは、この対象になり得ます。

そのため、CORSの許可がないという理由だけで、別サイトからの更新操作を防いだとは言えません。サーバーがフォームを受け取って処理した後、ブラウザが応答の読み取りだけを禁止する場合があります。更新APIではCSRFトークンや送信元の検証も設計します。

参照:出典 s1 / 出典 s2

Cookie付き通信は送信と読み取りの両方に条件がある

別オリジンへのfetchでCookieを送る構成では、画面側はcredentials: "include"を指定します。ただし、それだけで必ずCookieが送られるわけではありません。CookieのDomain・Path・Secure・SameSiteや、ブラウザの第三者Cookie制限などにも従います。

Cookie付きの応答を画面へ渡すには、API側がAccess-Control-Allow-Credentials: trueを返します。Access-Control-Allow-Originには、許可するオリジンを明記します。この場合に * は使えません。Cookieはブラウザが管理し、JavaScriptへSet-Cookieヘッダーを読ませることもありません。

説明用の通信例・設定例・擬似コード

// 正規画面 https://app.example からの説明用コード
fetch("https://api.example/profile", { credentials: "include" });

// APIのレスポンスヘッダー例
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Credentials: true
Vary: Origin

参照:出典 s1 / 出典 s2

送られたOriginをそのまま許可すると何が起きるか

次は設定不備を説明する仮定です。APIが、受け取ったOriginを確認せず許可値へコピーし、Cookie付き読み取りも許可するとします。攻撃者は自分のサイトのJavaScriptから、credentials: "include"を指定して被害者のプロフィールAPIを呼びます。

被害者がログイン中で、その通信にログイン用Cookieが送られる条件も満たすと、APIは被害者の住所などを返します。APIが攻撃者のオリジンを許可したため、ブラウザは応答を攻撃者のコードへ渡します。そのコードが住所を攻撃者の収集先へ送れば、攻撃者が取得できます。Cookieが送られない環境まで、この経路が成立すると断定はできません。

対策は、許可する画面のオリジンを固定の許可リストで照合することです。公開データへ * を指定する判断と、個人情報へ任意のOriginを反射する判断は区別します。

参照:出典 s1

CORSエラーは許可ヘッダーと実際の通信で調べる

開発者ツールで、Origin、本来のメソッド、OPTIONSの有無、APIが返した許可ヘッダーを確認します。エラー応答にも必要な許可がないと、画面からは元の401や500の本文を読めず、CORSエラーとして見えることがあります。

mode: "no-cors"を指定しても、任意のAPI応答が読めるようにはなりません。応答の内容をJavaScriptから参照できない制限付きの状態になります。

確認問題:APIキーを持たない外部プログラムをCORSで拒否できるでしょうか。答えはできません。CORSはブラウザの応答公開を制御する仕組みなので、API自身で認証・認可を実施します。

参照:出典 s1

出典・確認範囲

  1. [s1] Cross-Origin Resource Sharing (CORS)MDN / 確認日 2026-11-03
  2. [s2] Fetch Standard — CORS protocolWHATWG / 確認日 2026-11-03
未確認事項・限界

通信例・設定例・被害の例は、仕組みを理解するための仮定です。特定の製品で発生した事件を示すものではありません。ブラウザやサービス固有の設定は、利用する環境の公式資料でも確認してください。