CORSとは?
CORSとは、Cross-Origin Resource Sharing の略で、日本語では「オリジン間リソース共有」と呼ばれます。
簡単に言うと、
「別のサイト・別のドメイン・別のポートにあるデータへ、ブラウザからアクセスしてよいかをサーバー側が許可する仕組み」
です。
たとえば、次のような場合にCORSが関係します。
https://example.com
から
https://api.example.net/data
へJavaScriptでアクセスする
この2つは別のオリジンなので、ブラウザは勝手にデータを読み取らせません。サーバー側が「このサイトからのアクセスなら許可します」と返した場合だけ、ブラウザがレスポンスをJavaScriptに渡します。
W3CのCORS仕様でも、レスポンスに Access-Control-Allow-Origin を含めることで、特定のオリジンからリソース内容へのアクセスを許可できると説明されています。
そもそもオリジンとは?
オリジンとは、次の3つの組み合わせです。
プロトコル + ドメイン + ポート番号
たとえば、以下はすべて別オリジンです。
https://example.com
http://example.com
https://api.example.com
https://example.com:3000
https://example.com:8443
同じ example.com でも、http と https が違えば別オリジンです。
サブドメインが違う場合も別オリジンです。
https://example.com
https://www.example.com
https://api.example.com
これらも別扱いになります。
なぜCORSが必要なのか?
CORSが必要な理由は、ブラウザには「同一オリジンポリシー」という安全のための制限があるからです。
同一オリジンポリシーとは、
「あるWebサイトのJavaScriptが、別のサイトの情報を勝手に読み取れないようにする仕組み」
です。
たとえば、あなたがログイン中のサービスがあるとします。
悪意あるサイトを開いたとき、そのサイトのJavaScriptが勝手にログイン中のサービスへアクセスして、個人情報や管理画面のデータを読み取れたら危険です。
そのため、ブラウザは標準で「別オリジンのレスポンスをJavaScriptに見せない」という制限をかけています。
CORSは、その制限を完全に無効化するものではありません。
「このオリジンからのアクセスなら許可してよい」と、サーバーが明示した場合だけ、ブラウザがアクセスを認める仕組みです。
CORSの基本的な流れ
たとえば、以下のような構成があるとします。
フロント側
https://example.com
API側
https://api.example.net
フロント側のJavaScriptで次のようにAPIを呼び出します。
fetch("https://api.example.net/users")
このとき、ブラウザはAPIサーバーに対してリクエストを送ります。
その際、ブラウザはリクエストに Origin ヘッダーを付けます。
Origin: https://example.com
APIサーバーが許可する場合、レスポンスに次のようなヘッダーを付けます。
Access-Control-Allow-Origin: https://example.com
この値がリクエスト元のオリジンと一致していれば、ブラウザはJavaScriptにレスポンスを渡します。
一致しない場合、ブラウザはCORSエラーとしてブロックします。
Access-Control-Allow-Origin は、指定されたオリジンからのコードにレスポンスを共有できるかどうかを示すレスポンスヘッダーです。
CORSエラーとは?
CORSエラーとは、サーバーが返したレスポンスを、ブラウザがJavaScript側に渡さなかった状態です。
よくあるエラーは次のようなものです。
Access to fetch at 'https://api.example.net/data'
from origin 'https://example.com'
has been blocked by CORS policy
これは、
「リクエスト元のサイトは、APIサーバーから許可されていません」
という意味です。
重要なのは、CORSエラーはJavaScript側だけで完全に解決できる問題ではないという点です。
基本的には、APIを返すサーバー側でCORSヘッダーを設定する必要があります。
CORSでよく使うヘッダー
Access-Control-Allow-Origin
もっとも重要なヘッダーです。
Access-Control-Allow-Origin: https://example.com
これは、
「https://example.com からのアクセスを許可します」
という意味です。
すべてのオリジンを許可する場合は、次のように書くこともあります。
Access-Control-Allow-Origin: *
ただし、ログイン情報やCookieを扱う場合に * を使うのは危険です。資格情報を含むリクエストでは、ワイルドカードではなく具体的なオリジンを指定する必要があります。
Access-Control-Allow-Methods
許可するHTTPメソッドを指定します。
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
たとえば、POST を許可していないAPIに対してJavaScriptから POST すると、CORSで止まることがあります。
Access-Control-Allow-Headers
許可するリクエストヘッダーを指定します。
Access-Control-Allow-Headers: Content-Type, Authorization
たとえば、APIリクエストで Authorization ヘッダーを使う場合、サーバー側でこれを許可していないとCORSエラーになります。
MDNでも、Access-Control-Allow-Headers はプリフライトリクエストへのレスポンスで使われ、実際のリクエストで使用するHTTPヘッダーを示すものと説明されています。
Access-Control-Allow-Credentials
Cookieや認証情報を含める場合に使います。
Access-Control-Allow-Credentials: true
JavaScript側では、次のように指定します。
fetch("https://api.example.net/user", {
credentials: "include"
});
この場合、サーバー側には次のような設定が必要です。
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
このとき、次の設定は基本的に使えません。
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Cookieなどの資格情報を含むリクエストでは、Access-Control-Allow-Origin: * のレスポンスはブラウザにブロックされます。
Access-Control-Expose-Headers
通常、JavaScriptから読めるレスポンスヘッダーは限られています。
独自ヘッダーをJavaScript側で読みたい場合は、サーバー側で次のように指定します。
Access-Control-Expose-Headers: X-Total-Count
たとえば、APIの一覧取得で総件数を X-Total-Count に入れている場合、この設定がないとJavaScript側で値を読めないことがあります。
Access-Control-Max-Age
プリフライトリクエストの結果をどのくらいキャッシュするかを指定します。
Access-Control-Max-Age: 86400
毎回プリフライトが発生すると通信が増えるため、許可内容が変わらないAPIではキャッシュを使うことがあります。
プリフライトリクエストとは?
プリフライトリクエストとは、本番のリクエストを送る前に、ブラウザがサーバーへ確認する事前チェックです。
主に OPTIONS メソッドで送られます。
たとえば、JavaScriptから次のようなリクエストを送るとします。
fetch("https://api.example.net/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer token"
},
body: JSON.stringify({ name: "Taro" })
});
このような場合、ブラウザは先に次のような確認をします。
OPTIONS /users
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
サーバーが次のように返せば、ブラウザは本番の POST を送ります。
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization
つまり、プリフライトは、
「このメソッドとヘッダーでリクエストしても大丈夫ですか?」
とブラウザが事前に確認する仕組みです。
Origin ヘッダーはオリジン間アクセスリクエストやプリフライトリクエストのオリジンを示し、Access-Control-Request-Method や Access-Control-Request-Headers は実際のリクエストで使うメソッドやヘッダーをサーバーに知らせるために使われます。
CORSが発生しやすい場面
フロントエンドとAPIのドメインが違う場合
https://example.com
https://api.example.com
同じ会社や同じサイトのように見えても、サブドメインが違うため別オリジンです。
ローカル開発中
http://localhost:3000
http://localhost:8000
同じ localhost でも、ポート番号が違うため別オリジンです。
React、Vue、Next.js、Viteなどでフロントを開発し、別ポートでAPIを動かしている場合によく発生します。
WordPress REST APIを外部サイトから呼ぶ場合
たとえば、別サイトからWordPressのREST APIを呼び出す場合です。
https://site-a.com
から
https://site-b.com/wp-json/wp/v2/posts
へアクセス
この場合も別オリジンなので、API側でCORSの許可が必要になることがあります。
Google Apps Scriptや外部APIを呼ぶ場合
Webアプリ化したGAS、外部API、独自サーバーなどをブラウザから直接呼ぶ場合もCORSが関係します。
サーバー側がCORSヘッダーを返していないと、ブラウザ側では取得できません。
CORSでよくある誤解
CORSはサーバーへのアクセス自体を完全に止めるものではない
CORSは、主にブラウザがレスポンスをJavaScriptへ渡すかどうかの制御です。
サーバーにリクエスト自体が届くことはあります。
つまり、
「CORSで守っているからAPIは安全」
とは言えません。
API側では、認証、認可、CSRF対策、レート制限なども別途必要です。
Postmanでは成功するのにブラウザでは失敗する
これはよくあります。
Postmanやサーバー側プログラムは、ブラウザの同一オリジンポリシーに縛られません。
そのため、Postmanでは成功しても、ブラウザのJavaScriptから呼ぶとCORSエラーになることがあります。
JavaScript側だけで根本解決できない
CORSはサーバー側が許可ヘッダーを返す仕組みです。
そのため、基本的にはAPIサーバー側の設定が必要です。
フロント側で mode: "no-cors" を付けても、自由にレスポンスを読めるようになるわけではありません。
むしろレスポンスの中身をJavaScriptで扱えなくなることがあります。
CORSの設定例
すべてのオリジンを許可する場合
公開APIや画像、フォントなどで使われることがあります。
Access-Control-Allow-Origin: *
ただし、Cookieやログイン情報を扱うAPIでは避けるべきです。
特定のサイトだけ許可する場合
Access-Control-Allow-Origin: https://example.com
基本的にはこちらの方が安全です。
複数のオリジンを許可したい場合
Access-Control-Allow-Origin に複数のオリジンをそのまま並べることはできません。
悪い例です。
Access-Control-Allow-Origin: https://a.com, https://b.com
複数許可したい場合は、サーバー側でリクエストの Origin を確認し、許可リストに含まれていれば、そのOriginをそのまま返します。
Access-Control-Allow-Origin: https://a.com
または、
Access-Control-Allow-Origin: https://b.com
動的に Access-Control-Allow-Origin を返す場合は、キャッシュの誤動作を防ぐために Vary: Origin を付ける必要があります。Fetch Standardでも、CORS要件が Access-Control-Allow-Origin: * や静的なオリジン指定より複雑な場合は Vary を使うとされています。
Vary: Origin
CORSエラーの確認ポイント
CORSエラーが出たら、次を順番に確認します。
- アクセス元のオリジンは何か
- APIサーバーが
Access-Control-Allow-Originを返しているか - 返しているオリジンがアクセス元と一致しているか
POST、PUT、DELETEなどのメソッドが許可されているかContent-TypeやAuthorizationなどのヘッダーが許可されているか- Cookieを使う場合、
Access-Control-Allow-Credentials: trueがあるか - Cookieを使うのに
Access-Control-Allow-Origin: *になっていないか - プリフライトの
OPTIONSにサーバーが正しく応答しているか - CDNやキャッシュが古いCORSヘッダーを返していないか
- 動的にOriginを返す場合、
Vary: Originがあるか
CORSの一番重要な理解
CORSは、ブラウザとサーバーの間で行われる「許可確認」の仕組みです。
フロント側が、
このAPIのデータを読みたい
とリクエストします。
ブラウザが、
このサイトから別オリジンへアクセスしている
と判断します。
サーバーが、
このオリジンなら許可します
とCORSヘッダーで返します。
ブラウザが、
許可されているのでJavaScriptにレスポンスを渡します
と処理します。
反対に、サーバーが許可していなければ、ブラウザがレスポンスをJavaScriptに渡しません。
まとめ
CORSとは、別オリジンのリソースへブラウザからアクセスするときに、サーバー側が許可するための仕組みです。
重要なポイントは次の通りです。
CORSはブラウザの安全機能に関係する
オリジンは「プロトコル + ドメイン + ポート」で決まる
別オリジンへのアクセスではCORS確認が行われる
許可はサーバー側のHTTPヘッダーで行う
CORSエラーは基本的にフロント側だけでは直せない
Cookieやログイン情報を扱う場合は特に慎重に設定する
一言でまとめると、CORSは「別のサイトから自分のAPIやデータを読ませてもよいか」をサーバーがブラウザに伝えるためのルールです。
