CORSは、ブラウザが"別オリジンへのHTTPリクエストをどこまで許可するか"を制御する仕組みです。APIが動いているのにフロントエンドからだけ失敗する場合、CORSが原因であることがよくあります。
CORSは、ブラウザ上のJavaScriptが別オリジンのリソースへ自由にアクセスできないようにしつつ、必要な場合だけサーバー側の許可で通信を成立させる仕組みです。
CORSは Cross-Origin Resource Sharing の略です。重要なのは、CORSは HTTPそのものの制限というより、主にブラウザのセキュリティ制約 だという点です。
たとえば次のような構成を考えます。
https://app.example.comhttps://api.example.comこのとき、ブラウザで動くJavaScriptから見ると、app.example.com と api.example.com は別オリジンです。そのため、APIサーバーが適切に許可しない限り、ブラウザはレスポンスをJavaScriptに渡せません。
CORSを理解するには、まず"オリジン"の意味を正確に押さえる必要があります。オリジンはスキーム、ホスト、ポートの組み合わせです。
| URL | オリジン |
|---|---|
https://example.com | https + example.com + 443 |
https://api.example.com | https + api.example.com + 443 |
http://example.com | http + example.com + 80 |
https://example.com:8443 | https + example.com + 8443 |
次は別オリジンです。
https://example.com と https://api.example.com(ホストが違う)http://example.com と https://example.com(スキームが違う)https://example.com と https://example.com:8443(ポートが違う)ブラウザには"同一オリジンポリシー"があり、別オリジンへの自由なアクセスを制限します。CORSはその例外を安全に許可する仕組みです。
同一オリジンポリシーがないと、悪意あるサイトのJavaScriptが別サイトの情報へ勝手にアクセスしやすくなります。CORSは、この制限を完全になくすのではなく、サーバーが明示的に許可したときだけ緩める ための仕組みです。
CORSエラーは"ネットワーク接続自体が失敗した"とは限りません。サーバーが応答していても、ブラウザがレスポンスをJavaScriptへ渡さないことで失敗に見えることがあります。
よくある誤解は「CORSエラー = APIが落ちている」という理解です。実際には次のようなことが起きます。
そのため、サーバーログ上は 200 OK でも、フロントエンドでは失敗に見えることがあります。
CORSでは、サーバーが返すレスポンスヘッダーが中心です。最初に覚えるべきなのは Access-Control-Allow-Origin です。
| ヘッダー | 主な役割 |
|---|---|
Access-Control-Allow-Origin | どのオリジンを許可するか |
Access-Control-Allow-Methods | 許可するHTTPメソッド |
Access-Control-Allow-Headers | 許可するリクエストヘッダー |
Access-Control-Allow-Credentials | Cookieや認証情報付き通信を許可するか |
Access-Control-Max-Age | プリフライト結果をどれくらいキャッシュするか |
Access-Control-Allow-OriginAccess-Control-Allow-Origin: https://app.example.com
この場合、https://app.example.com から来たブラウザのJavaScriptは、条件を満たせばレスポンスを利用できます。
すべてを許可する例:
Access-Control-Allow-Origin: *
ただし * は便利ですが、認証付き通信とは相性が悪いため注意が必要です。
Access-Control-Allow-CredentialsCookieや認証情報を伴うクロスオリジン通信では、Access-Control-Allow-Credentials: true が必要です。この場合、* との組み合わせは使えません。
Access-Control-Allow-Credentials: true
認証付き通信では、Access-Control-Allow-Origin に明示的なオリジン指定が必要です。
CORSでは、すべてのリクエストが同じ扱いではありません。比較的単純なものはそのまま送られ、条件が複雑なものは先に"プリフライト"が走ります。
ブラウザは、リクエスト内容によって次の2系統で動きます。
条件が比較的限定されたリクエストでは、ブラウザは事前確認なしで本リクエストを送り、レスポンスのCORSヘッダーを見て許可判定します。
次のようなケースは単純リクエストとして扱われやすいです。
GETPOSTContent-Type が限定された値GET /profile HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Content-Type: application/json
{"name":"Taro"}
PUT、DELETE、JSON送信、独自ヘッダーなどを使うと、ブラウザは本リクエストの前に OPTIONS で事前確認を行うことがあります。これがプリフライトです。
典型的にプリフライトが発生する条件:
PUT や DELETE を使うContent-Type: application/jsonAuthorization ヘッダーを付けるプリフライトリクエストの例:
OPTIONS /products HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization
プリフライトレスポンスの例:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 600
Origin ヘッダーの役割CORS判定では、ブラウザが付与する Origin ヘッダーが重要です。サーバーはこれを見て、どのオリジンから来たかを判断します。
Origin: https://app.example.com
Origin はブラウザが自動で付与します。サーバーはこの値を見て、許可対象なら Access-Control-Allow-Origin を返します。
Cookieや認証ヘッダーを使うクロスオリジン通信では、通常より条件が厳しくなります。特に * と credentials の組み合わせは使えません。
このとき必要になるもの:
Access-Control-Allow-Origin: https://app.example.com + Access-Control-Allow-Credentials: truefetch(..., { credentials: "include" })fetch("https://api.example.com/me", {
method: "GET",
credentials: "include"
})
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
* が使えない理由認証付きCORSでは、どのオリジンでもよいという設定は危険なため、ワイルドカード指定はブラウザに拒否されます。
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
この組み合わせはブラウザに拒否されます。
CORSは"とりあえず全部許可"でも動きますが、実務では必要なオリジン、メソッド、ヘッダーに絞る方が安全です。
| 環境 | 許可オリジン例 |
|---|---|
| 開発 | http://localhost:3000 |
| ステージング | https://stg-app.example.com |
| 本番 | https://app.example.com |
推奨する方針:
Allow-Credentials を適切に使うOPTIONS を正しく返す| 症状 | 原因になりやすい点 |
|---|---|
No 'Access-Control-Allow-Origin' header | 許可ヘッダー未設定 |
| プリフライト失敗 | OPTIONS が通らない、Allow-Methods 不足 |
Authorization 付きで失敗 | Allow-Headers に Authorization がない |
| Cookie付きで失敗 | Allow-Credentials 不足、* を使っている |
| 開発だけ成功、本番だけ失敗 | 許可オリジンの環境差分 |
CORSはサーバー間通信やCLIでは通常問題になりません。ブラウザでだけ起きる点を忘れると切り分けを誤りやすいです。
curl では成功する、Postmanでも成功する、でもブラウザのフロントエンドからは失敗する、というのはCORSの典型的な現象です。curl や Postman は通常ブラウザのCORS制約を受けないためです。
CORS調査では、ブラウザのNetworkタブで OPTIONS、Origin、Access-Control-Allow-* を確認するのが基本です。
{{image:devtools_cors_options}}
見るべきポイント:
OPTIONS が飛んでいるかOrigin があるかAccess-Control-Allow-Origin があるかAccess-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Allow-Credentials204、200、403、404、500)curl でヘッダーを見る例# 単純リクエスト相当の確認
curl -i \
-H "Origin: https://app.example.com" \
https://api.example.com/profile
# プリフライト相当の確認
curl -i -X OPTIONS \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type, authorization" \
https://api.example.com/products
CORSはフロントエンド都合の一時的な設定ではなく、公開範囲を決めるセキュリティ設定として扱うべきです。
* を安易に使わないCORSは、ブラウザが別オリジンへのアクセスを安全に制御するための仕組みです。Access-Control-Allow-Origin を中心に、プリフライト、認証付き通信、OPTIONS の扱いを理解すると、フロントエンドとAPIの接続トラブルを切り分けやすくなります。
Access-Control-Allow-Origin などで許可を返すOPTIONS のプリフライトが発生するAllow-Credentials と明示的オリジン指定が重要curl で成功しても、ブラウザではCORSで失敗することがあるOrigin、OPTIONS、Access-Control-Allow-* を確認する