WEB PROTOCOL

CORS(Cross-Origin Resource Sharing)

異なるドメイン間の API リクエストをブラウザが安全に許可するための仕組み。

INTERACTIVE VISUALIZATION
OPTIONSプリフライト
リクエスト
レスポンス
CORSエラー
プロトコル
CORS
Cross-Origin Resource Sharing
Browser
アイドル
ブラウザ状態
Server
待機中
サーバー状態
進捗
1 / 8
なぜ CORS が必要か
シナリオ
CORSのシンプルリクエスト(プリフライト不要)のフロー。GET/HEAD/POSTでContent-Typeがtext/plain, multipart/form-data, application/x-www-form-urlencodedの場合
ステップ1 / 8
自動再生でCORSリクエストの流れを順番に確認できます
あなたのアプリ(https://app.example.com)から別ドメインの API(https://api.example.com)にデータを取りに行きたいとします。しかしブラウザは、セキュリティのために「別ドメインへのリクエスト」をデフォルトでブロックします(同一オリジンポリシー)。CORS は、API サーバーが「このドメインからのアクセスなら許可するよ」とブラウザに伝える仕組みです。
通信状態
Browserアイドル
リクエスト未送信
Server待機中
リクエスト待機中
解説

📌
CORSとは

CORS(Cross-Origin Resource Sharing)は、ブラウザが異なるオリジン間でのHTTPリクエストを安全に制御するための仕組みです。マンションに例えてみましょう。同じマンション(同一オリジン)の住人なら自由に行き来できますが、別のマンション(別オリジン)の部屋に入るには、その部屋の住人から「許可証」をもらう必要があります。CORSヘッダーはこの「許可証」に相当します。

同一オリジンポリシー(Same-Origin Policy)とは、ブラウザに組み込まれたセキュリティ機能です。あるオリジン(スキーム + ホスト + ポート)から読み込まれたスクリプトが、別のオリジンのリソースに自由にアクセスすることを制限します。
例: https://app.example.com:443 と https://api.example.com:443 はホストが異なるため「別オリジン」です。

上のツールで「シンプルリクエスト」シナリオを再生すると、OriginヘッダーとAccess-Control-Allow-Originヘッダーのやり取りが確認できます。

📌
特徴

  • 🔒同一オリジンポリシーブラウザはデフォルトでクロスオリジンリクエストをブロックします。同じスキーム(http/https)、同じホスト、同じポート番号の組み合わせを「同一オリジン」と呼び、これが一致しないリクエストはすべてクロスオリジンとして扱われます。サーバー側の設定がなければ、fetchやXMLHttpRequestでのクロスオリジンアクセスは失敗します。
  • ✈️プリフライトリクエストPUT、DELETE、PATCHメソッドや、AuthorizationやContent-Type: application/jsonなどの非シンプルヘッダーを使う場合、ブラウザは自動的にOPTIONSリクエスト(プリフライト)を送信します。サーバーが許可を返して初めて本リクエストが送信されます。これにより、サーバーが予期しないリクエストを受け取ることを防ぎます。上のツールで「プリフライトリクエスト」シナリオを選択すると、この2段階のフローが確認できます。
  • 📋Access-Controlヘッダー群Access-Control-Allow-Origin(許可するオリジン)、Access-Control-Allow-Methods(許可するHTTPメソッド)、Access-Control-Allow-Headers(許可するリクエストヘッダー)、Access-Control-Max-Age(プリフライトのキャッシュ時間)など、複数のレスポンスヘッダーでアクセス制御を行います。「*」を指定するとすべてのオリジンを許可できますが、credentials(Cookie等)使用時は具体的なオリジンの指定が必要です。
  • 🍪credentials(資格情報)デフォルトではクロスオリジンリクエストにCookieやAuthorizationヘッダーは含まれません。credentials: "include"を指定するとCookieを送信でき、サーバー側でAccess-Control-Allow-Credentials: trueを返す必要があります。この場合、Access-Control-Allow-Originに「*」は使えず、具体的なオリジンを指定する必要があります。

📌
ユースケース

⚛️ React + APIサーバー分離
フロントエンド(Vercel: https://app.example.com)とバックエンド(Railway: https://api.example.com)を別々にデプロイする構成では、フロントからAPIへのリクエストがすべてクロスオリジンになります。APIサーバーにCORSヘッダーを設定することで、フロントエンドからのアクセスを許可します。Next.jsのAPI Routesを使えばCORSを回避できますが、独立したAPIサーバーが必要な場合はCORS設定が不可欠です。
🖼️ CDNからのフォント/画像読み込み
Google Fonts(fonts.googleapis.com)やCDNからのWebフォント読み込みでは、CSSの@font-faceで指定されたフォントファイルへのリクエストがクロスオリジンになります。CDN側でAccess-Control-Allow-Origin: * が設定されているため、どのオリジンからでもフォントを読み込めます。Canvas APIで外部画像を操作する場合もCORSが必要です。
🏗️ マイクロサービス間通信
AWS API Gatewayを使ったマイクロサービスアーキテクチャでは、ブラウザから複数のサービス(認証、ユーザー管理、決済等)に直接リクエストする場合があります。各サービスのAPIエンドポイントが異なるドメインやパスにあるため、CORSの設定が必要です。API Gatewayレベルでまとめて設定できます。
🔌 サードパーティAPI連携
Stripe(決済)やGitHub API(OAuth)などの外部サービスと連携する場合、ブラウザから直接APIを呼ぶとクロスオリジンリクエストになります。多くのサービスはCORSを適切に設定していますが、APIキーを含むリクエストではサーバーサイドプロキシ経由でアクセスすることが推奨されます。

📌
用語解説

オリジン(Origin)
= スキーム + ホスト + ポート
URLの「スキーム(https)」「ホスト(example.com)」「ポート(443)」の3つの組み合わせをオリジンと呼びます。この3つがすべて一致するリクエストが「同一オリジン」、1つでも異なれば「クロスオリジン」です。パスやクエリパラメータはオリジンの判定に含まれません。
https://example.com:443scheme + host + port
同一オリジンポリシー
= Same-Origin Policy
ブラウザに組み込まれたセキュリティ制約で、あるオリジンから読み込まれたドキュメントやスクリプトが、別のオリジンのリソースにアクセスすることを制限します。これにより、悪意のあるサイトが別サイトのCookieやデータを盗むことを防ぎます。CORSはこの制約を安全に緩和するための仕組みです。
SameCross×blocked by default
プリフライト(Preflight)
= OPTIONSリクエスト
本リクエスト送信前にブラウザが自動的に送信するOPTIONSメソッドのリクエストです。「このメソッドとヘッダーを使ってリクエストしてよいか?」をサーバーに事前確認します。サーバーがAccess-Control-Allow-MethodsとAccess-Control-Allow-Headersで許可を返せば、本リクエストが送信されます。
BrowserOPTIONSServerAllow
Access-Control-Allow-Origin
= 許可オリジン指定
サーバーがレスポンスに付与するヘッダーで、どのオリジンからのアクセスを許可するかを指定します。特定のオリジン(例: https://app.example.com)または「*」(すべて許可)を指定できます。ブラウザはこのヘッダーの値とリクエスト元のオリジンを比較し、一致しなければレスポンスをブロックします。
Access-Control-Allow-Origin:https://app.example.com
Allow-Methods / Allow-Headers
= 許可メソッド・ヘッダー
プリフライトレスポンスで返されるヘッダーです。Access-Control-Allow-Methodsは許可するHTTPメソッド(GET, POST, PUT, DELETE等)を、Access-Control-Allow-Headersは許可するリクエストヘッダー(Authorization, Content-Type等)を指定します。ブラウザはこれらを確認し、本リクエストが許可されているか判断します。
GET, PUT, DELETEAuthorization, Content-Type
Access-Control-Max-Age
= プリフライトキャッシュ時間
プリフライトレスポンスをブラウザがキャッシュする秒数を指定します。例えば3600を指定すると、同じリクエストパターンに対して1時間はプリフライトが省略されます。キャッシュにより2回目以降のリクエストが1往復で完了し、パフォーマンスが向上します。ブラウザごとに上限があり、Chromeは最大7200秒(2時間)です。
3600scache

⚖️
シンプルリクエスト vs プリフライトリクエスト

CORS のリクエストは「シンプルリクエスト」と「プリフライト付きリクエスト」の2種類に分かれます。どちらになるかはブラウザが自動で判断するため、開発者が選ぶものではありません。

判断基準はシンプルで、HTTP メソッドとヘッダーが「安全」な範囲内かどうかです。

シンプルリクエスト
プリフライトなしで1往復で完了します。以下の条件をすべて満たす場合に該当します。
条件
- メソッドが GET / HEAD / POST のいずれか
- ヘッダーが Accept, Content-Language 等の基本的なもののみ
- Content-Type が text/plain, multipart/form-data, application/x-www-form-urlencoded のいずれか
ユースケース
- 公開 API からデータを取得(GET)
- 画像や CSS を CDN から読み込む
- シンプルなフォーム送信(POST)
プリフライト付きリクエスト
OPTIONS で事前確認してから本リクエストを送る2往復のフローです。シンプルリクエストの条件から外れた場合に自動的にこちらになります。
トリガーになる条件
- メソッドが PUT / DELETE / PATCH
- Authorization ヘッダーを付ける
- Content-Type: application/json を使う
- カスタムヘッダー(X-Custom-Header 等)を付ける
ユースケース
- REST API で JSON データを送受信
- 認証トークン付きの API 呼び出し
- データの更新・削除(PUT / DELETE)
// シンプルリクエスト(プリフライトなし)
fetch("https://api.example.com/users")
// → GET + 基本ヘッダーのみ → 1往復で完了
// プリフライトが発生するリクエスト
fetch("https://api.example.com/users/123", {
method: "PUT",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer token..."
},
body: JSON.stringify({ name: "田中" })
}
// → PUT + JSON + Auth → OPTIONS(事前確認)→ PUT(本リクエスト)
プリフライトのキャッシュ: サーバーが Access-Control-Max-Age: 3600 を返すと、ブラウザはプリフライトの結果を1時間キャッシュします。同じドメインへの同じ種類のリクエストは、2回目以降プリフライトなしで送信できるため、実質シンプルリクエストと同じ速度になります。

📋
CORS で使うヘッダー

CORS は HTTP ヘッダーでやり取りします。リクエスト側(ブラウザが自動付与)レスポンス側(サーバーが設定)に分かれます。

リクエストヘッダー(ブラウザが自動で付ける)
Origin

リクエスト元のオリジン(スキーム + ドメイン + ポート)を伝えます。ブラウザが自動で付けるため、JavaScript から変更や削除はできません。サーバーはこの値を見て許可するかどうかを判断します。

Origin: https://app.example.com
Access-Control-Request-Method

プリフライト(OPTIONS)リクエストでのみ使われます。本リクエストで使いたい HTTP メソッドをサーバーに伝えます。

Access-Control-Request-Method: PUT
Access-Control-Request-Headers

プリフライトで使われます。本リクエストで使いたいカスタムヘッダーをサーバーに伝えます。

Access-Control-Request-Headers: Authorization, Content-Type
レスポンスヘッダー(サーバーが設定する)
Access-Control-Allow-Origin

最も重要な CORS ヘッダーです。どのオリジンからのアクセスを許可するかを指定します。ブラウザはこの値とリクエスト元の Origin を比較し、一致しなければレスポンスを JavaScript から読めなくします。

// 特定のオリジンだけ許可(推奨)
Access-Control-Allow-Origin: https://app.example.com
// 全オリジン許可(公開 API 向け)
Access-Control-Allow-Origin: *
// ⚠ * と credentials: "include" は併用できない

* は手軽ですが、Cookie や Authorization を送る場合は使えません。本番環境では具体的なオリジンを指定するのが安全です。

Access-Control-Allow-Methods

プリフライトレスポンスで使います。許可する HTTP メソッドの一覧を返します。

Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers

プリフライトレスポンスで使います。許可するリクエストヘッダーの一覧を返します。

Access-Control-Allow-Headers: Authorization, Content-Type, X-Custom
Access-Control-Allow-Credentials

Cookie や Authorization ヘッダーをクロスオリジンで送受信するかを制御します。true にすると、クライアント側で credentials: "include" を指定した場合に Cookie が送受信されます。

Access-Control-Allow-Credentials: true
// ⚠ この場合 Allow-Origin: * は使えない
// 具体的なオリジンの指定が必須
Access-Control-Max-Age

プリフライトの結果をブラウザがキャッシュする秒数です。この間は同じ種類のリクエストでプリフライトが省略されます。

Access-Control-Max-Age: 3600
// 1時間キャッシュ → 2回目以降プリフライト不要
Access-Control-Expose-Headers

デフォルトでは JavaScript から読めるレスポンスヘッダーは限られています(Cache-Control, Content-Type 等のみ)。カスタムヘッダーを JavaScript から読みたい場合に指定します。

Access-Control-Expose-Headers: X-Total-Count, X-Request-Id
サーバー側の設定例(Express.js)
// Express.js + cors ミドルウェア
const cors = require("cors")
app.use(cors({
origin: "https://app.example.com",
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Authorization", "Content-Type"],
credentials: true,
maxAge: 3600
}))

📌
CORSの手順(プリフライト)

プリフライトが必要なCORSリクエストの5つのステップを追いかけます。

シナリオ: フロントエンド(https://app.example.com)から API サーバー(https://api.example.com)のユーザー情報を更新する。PUT メソッド + JSON + 認証トークンを使うため、プリフライトが発生します。
1
ブラウザがプリフライト(OPTIONS)を自動送信
ブラウザが「PUT + JSON + Authorization を使いたいのですが許可されていますか?」と事前確認します。開発者が送るのではなく、ブラウザが自動で判断して送信します。
OPTIONS /users/123 HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Authorization, Content-Type
2
サーバーが許可リストをチェック
サーバーは CORS 設定(Express の cors() ミドルウェアや Nginx の設定など)と照合して、(1) Origin が許可リストに含まれるか、(2) PUT メソッドが許可されているか、(3) Authorization と Content-Type ヘッダーが許可されているか、をチェックします。
3
サーバーが許可情報を返す
すべて OK なら、サーバーが許可ヘッダー付きで 204 No Content を返します。Max-Age: 3600 は「この許可を1時間キャッシュしていい」という意味です。
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 3600
4
ブラウザが本リクエスト(PUT)を送信
プリフライトで許可が確認できたので、本来送りたかったリクエストを送信します。JSON データと認証トークンが含まれます。
PUT /users/123 HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Authorization: Bearer eyJhbGciOi...
Content-Type: application/json
{"name": "田中太郎", "email": "tanaka@example.com"}
5
サーバーがレスポンスを返す
サーバーがデータを更新し、結果を返します。レスポンスにも Access-Control-Allow-Origin が付いており、ブラウザがこれを検証して JavaScript にデータを渡します。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Content-Type: application/json
{"id": 123, "name": "田中太郎", "updated": true}
OPTIONSAllowPUTResponseプリフライト + 本リクエストの2往復

📌
よくあるCORSエラーと対策

Access to fetch at 'https://api.example.com/users' from origin 'https://app.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
よくあるエラー原因
  • -サーバーにAccess-Control-Allow-Originヘッダーが設定されていない
  • -Access-Control-Allow-Originの値がリクエスト元のオリジンと一致しない
  • -credentials使用時にAccess-Control-Allow-Originが「*」になっている
  • -プリフライトでAccess-Control-Allow-Methodsに必要なメソッドが含まれていない
  • -OPTIONSリクエストに対してサーバーが200/204以外を返している
対策
  • -サーバー側でAccess-Control-Allow-Origin: https://app.example.com を設定する
  • -Express: cors()ミドルウェア、Nginx: add_header、API Gateway: CORS設定で対応
  • -credentials使用時は具体的なオリジンを指定し、Allow-Credentials: trueを追加
  • -OPTIONSリクエストをハンドリングし、Allow-Methods/Headersを返す
  • -開発時はプロキシ(Next.js rewrites、Vite proxy)でCORSを回避可能
上のツールで両方のシナリオを再生して、シンプルリクエスト(1往復)とプリフライトリクエスト(2往復)の違いを確認してください。

関連コンテンツ

HTTP通信

HTTP通信

ブラウザとサーバー間のHTTPリクエスト/レスポンスの仕組みを可視化

HTTP/1.1 vs HTTP/2

HTTP/1.1 vs HTTP/2

直列リクエストと多重化の違いをウォーターフォールチャートで比較

セッション vs Cookie vs JWT

セッション vs Cookie vs JWT

認証方式の違いと使い分けを可視化

CDNエッジキャッシュ

CDNエッジキャッシュ

CDNのエッジキャッシュとオリジンサーバーの関係と仕組みを可視化

ブラウザキャッシュ

ブラウザキャッシュ

Cache-Control、ETag、条件付きリクエストによるブラウザキャッシュの仕組みを可視化

HTTPステータスコード

HTTPステータスコード

1xx〜5xxの各グループと代表的なステータスコードの意味を可視化