今回やること
CORSエラーは、Consoleに長い英文で表示されます。
CORSエラーでは、Consoleの文だけでなく、Networkで実際のリクエストとレスポンスヘッダーを見ます。
Step 1: ConsoleでCORSか確認する
Consoleに次のような文が出ていたら、CORSが関係しています。
Access to fetch at 'https://api.example.com' from origin 'https://app.example.com'
has been blocked by CORS policy
ここで見るのは、2つのURLです。
- どのページから
- どのAPIへ
オリジンが違う時、ブラウザはCORSのルールを確認します。
Step 2: Networkで該当リクエストを探す
DevToolsのNetworkを開き、Fetch/XHRに絞ります。
見る項目:
- Request URL
- Request Method
- Status
- Response Headers
- Request Headers
Consoleだけでなく、Networkで該当通信がどう扱われているかを確認します。
Step 3: Originを見る
Request Headersに Origin が入っていることがあります。
Origin: https://app.example.com
これは、ブラウザが「このページからAPIへアクセスしようとしている」とサーバへ伝えるヘッダーです。
API側は、このOriginを許可するかどうか判断します。
Step 4: レスポンスヘッダーを見る
API側がCORSを許可する場合、レスポンスに次のようなヘッダーを返します。
Access-Control-Allow-Origin: https://app.example.com
認証Cookieなどを含む場合は、追加で次のような設定も関係します。
Access-Control-Allow-Credentials: true
ただし、credentials を使う場合に Access-Control-Allow-Origin: * は使えません。
Step 5: preflightを見る
POSTや独自ヘッダーを使う通信では、本番のリクエスト前に OPTIONS リクエストが送られることがあります。
これをpreflightと呼びます。
OPTIONS /api/users
POST /api/users
preflightが失敗すると、実際のPOSTが送られないことがあります。
Networkで OPTIONS が出ていないか確認します。
Step 6: よくある原因
| 症状 | よくある原因 |
|---|---|
| CORS blocked | API側でOriginを許可していない |
| preflight失敗 | OPTIONSに対応していない |
| Cookieが送られない | credentials設定不足 |
| localhostだけ失敗 | 許可Originにlocalhostがない |
| 本番だけ失敗 | ドメイン設定が古い |
フロントだけでは直せないことが多い
CORSは、基本的にAPI側が許可する仕組みです。
フロント側でURLを変えたり、modeを変えたりして無理に回避するものではありません。
必要な確認:
- API側で許可Originを設定しているか
- OPTIONSに応答しているか
- 許可methodにPOSTなどが含まれるか
- 許可headersに必要なヘッダーが含まれるか
注意: CORSを避けるために全Originを雑に許可すると、意図しないサイトからAPIを使われる可能性があります。
まとめ
CORSエラーでは、Consoleのエラー文、NetworkのRequest URL、Origin、preflight、レスポンスヘッダーを確認します。
「ブラウザが止めている」のか、「APIが落ちている」のか、「preflightで止まっている」のかを分けると、修正箇所が見えやすくなります。
次に読む記事
CORS専用の確認方法
| 観測 | 判断 | 次に見るもの |
|---|---|---|
| OPTIONS失敗、POSTなし | preflightで停止 | 許可method・header |
| POSTは200、Consoleで遮断 | 実応答の許可不足 | Allow-Origin |
| Cookie付きだけ失敗 | credential不一致 | Allow-Credentials |
| redirect後に失敗 | 転送先が別origin | Locationと転送先設定 |
preflight失敗時、予定していたPOSTはまだ送信されていません。 OPTIONSと本リクエストを別行で読みます。localhostもportまで含めてoriginなので、3000番と4321番は別です。
修正後は許可originだけでなく、未許可originが拒否されることも確認します。認証APIではCookieなし、正常Cookie、期限切れCookieを分けます。HAR共有時はCookie、Authorization、tokenを除去します。完了条件はConsoleが静かなことではなく、許可境界どおりに成功と拒否が分かれることです。

参考リソース
ケース:認証Cookie付きAPIだけ失敗する
公開GETは成功するのにsessionを使うプロフィール取得だけ失敗する場合、OriginとCookie送信を別々に見ます。fetchにcredentials: "include"がなくCookieが未送信ならresponse headerだけ変えても認証されません。Cookieが送られAPIが200でも、Allow-CredentialsがなくAllow-Originが*ならbrowserはresponseをscriptへ渡しません。
OPTIONSのstatus、実GETの有無、GETのstatus、response header、Cookie有無を転記します。401ならserverは受信して認証拒否しています。GETがなくCORS文だけならpreflight、GETが200でも読めないなら実応答のCORS headerです。
本番だけなら正式domainの許可漏れ、proxyがOPTIONSだけ別serverへ流す、CDN cacheがOrigin別応答を分けない可能性があります。Vary: Originも確認します。完了時は正式originの認証取得、未許可originの拒否、認証なしの仕様どおりの応答を確認します。全面許可で見かけ上直した設定をこれで区別できます。
レビューで確認するCORS契約
許可originが検証済みlistか、requestのOriginをそのまま反射していないかを見ます。credential endpointではwildcardを使えません。不要なPUTやDELETE、無制限のheaderを許していないかも確認します。preflight cacheを長くする場合は設定反映の遅延も考えます。
development、preview、productionは別のorigin集合として管理します。suffix一致は意図しないhostを通す実装になり得ます。URL parserでscheme、hostname、portを比較し、正規化後の完全一致を基本にします。null originを理由なく許可しません。
server logにはOrigin、判定、request IDを残せますがcredentialは記録しません。OPTIONSへ認証を必須にして本request前に拒否する構成もあるためmiddleware順を見ます。proxyとapplicationの二重設定でAllow-Originが複数付かないよう、最終的にbrowserへ届くheaderを確認します。
WebSocket、画像要素、通常のnavigationはfetchと同じCORS挙動とは限りません。問題のresource種別と利用APIを明示してから仕様を参照します。
← 一覧に戻る