ブラウザでCORSエラーをデバッグする方法
fetchリクエストを実行し、コンソールを開くと、次のようなメッセージが表示されます:
Access to fetch at ‘https://api.example.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.
サーバーヘッダーをやみくもに変更する前に、一度立ち止まりましょう。ブラウザでのCORSデバッグの大部分は、ブラウザがすでに伝えている内容を読み取ることに尽きます — どこを見るべきかを知っていれば。
重要なポイント
- CORSエラーはサーバーではなく、ブラウザによって強制されます。リクエストは多くの場合サーバーに到達しており、ブロックはレスポンスの段階で発生します。
- ブラウザのコンソールメッセージが主要な診断ツールです — 失敗したルールやヘッダーを指し示すことが多いですが、根底にあるネットワーク、TLS、またはポリシーの問題を反映している場合もあります。
- プリフライトOPTIONSリクエストは、注意して見ていないと静かに失敗します。必ずネットワークタブを「すべて」でフィルタリングして確認しましょう。
- CORSのように見えるエラーがすべて実際にCORSの問題とは限りません。混在コンテンツ、TLS障害、リダイレクト、ブラウザ拡張機能など、すべてがCORSの問題に偽装する可能性があります。
CORSエラーが実際に意味すること
CORS(Cross-Origin Resource Sharing)エラーは、サーバーのレスポンスに適切な許可が含まれていないため、ブラウザがクロスオリジンリクエストをブロックしたときに発生します。重要なのはブラウザという言葉です — リクエストは多くの場合サーバーに到達しています。ブロックは戻りの段階で発生します。
オリジンは、プロトコル、ドメイン、ポートの組み合わせによって定義されます。したがって、http://localhost:3000とhttp://localhost:4000は、同じマシン上であっても異なるオリジンです。
DevToolsを使用してCORSエラーをデバッグする方法
DevTools → ネットワークタブを開きます(F12またはCmd+Option+I)。失敗したリクエストを再現します。その後:
- 失敗したリクエストを見つける — 赤いエントリを探すか、「Fetch/XHR」でフィルタリングします。
- コンソールタブを確認する — そこに表示されるエラーメッセージは具体的です。どのヘッダーが欠落しているか、または不一致かを示します。
- リクエストをクリック → ヘッダータブ — リクエストヘッダー(特に
Origin)とレスポンスヘッダー(特にAccess-Control-Allow-Origin)の両方を検査します。
コンソールエラーが主要な診断ツールです。最新のChromeとFirefoxは、次のような正確なメッセージを提供します:
- “No ‘Access-Control-Allow-Origin’ header is present” → サーバーがCORSヘッダーをまったく送信していません。
- “The value of ‘Access-Control-Allow-Origin’ must not be the wildcard ’*’ when credentials mode is ‘include’” →
credentials: 'include'を使用していますが、サーバーが*で応答しました。 - “Method PUT is not allowed by Access-Control-Allow-Methods” → プリフライトがHTTPメソッドを拒否しました。
プリフライトリクエスト(OPTIONS)のデバッグ
リクエストが非シンプルメソッド(PUT、DELETE、PATCH)、カスタムヘッダー、または仕様で許可されている数種類以外のコンテンツタイプを使用する場合、ブラウザは最初にプリフライトOPTIONSリクエストを送信します。これが失敗すると、実際のリクエストは送信されません。
ネットワークタブで「すべて」でフィルタリングし、メインリクエストの直前に同じURLへのOPTIONSリクエストを探します。それをクリックして確認します:
- リクエストヘッダー:
Access-Control-Request-MethodとAccess-Control-Request-Headers— これらは実際のリクエストが必要とするものをサーバーに伝えます。 - レスポンスヘッダー:
Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Allow-Origin— サーバーはそれぞれを明示的に許可する必要があります。
OPTIONSリクエストが4xxまたは5xxステータスコードを返す場合、それはCORSヘッダーの設定ミスではなく、サーバー側のルーティングまたは設定エラーである可能性が高いです。まず根本的なエラーを修正してください。
Discover how at OpenReplay.com.
本当にCORSエラーなのか?
CORSとして表面化するエラーがすべてCORSによって引き起こされているわけではありません。よくある誤検出には以下が含まれます:
- 混在コンテンツ —
https://ページからhttp://を取得することは、CORSヘッダーに関係なく、ブラウザの混在コンテンツポリシーによってブロックされます。 - TLS/証明書エラー — TLSハンドシェイクの失敗は、コンソールではCORS障害のように見えることがあります。
- 異なるオリジンへのリダイレクト — サーバーがリクエストを新しいオリジンにリダイレクトすると、ブラウザはリクエストをブロックします。CORSリクエストではクロスオリジンリダイレクトは許可されていません。
- ブラウザ拡張機能 — プライバシーまたは広告ブロック拡張機能は、CORSが評価される前にリクエストをキャンセルすることがあります。
- サードパーティCookie制限 —
credentials: 'include'を使用している場合、CORSヘッダーが完全に正しくても、最新のブラウザはサードパーティオリジンからのCookieをブロックする可能性があります。
実際の原因を特定するには、拡張機能を無効にしたプライベート/シークレットウィンドウでリクエストを試してください。
認証情報付きリクエスト: よくある落とし穴
fetch(url, { credentials: 'include' })を使用する場合、サーバーはAccess-Control-Allow-Origin: *で応答できません。正確なリクエスト元のオリジンをエコーバックする必要があります。サーバーはまた、レスポンスにAccess-Control-Allow-Credentials: trueを含める必要があります。どちらか一方でも欠けると、ブラウザはレスポンスをブロックします。
CORSデバッグクイックチェックリスト
| 確認項目 | 確認内容 |
|---|---|
| コンソールエラーメッセージ | 失敗した正確なヘッダーまたはルール |
| OPTIONSリクエストの有無 | プリフライトがトリガーされた — そのレスポンスを確認 |
レスポンスにAccess-Control-Allow-Originがあるか | オリジンと一致するか*である必要がある |
| 認証情報を使用しているか | *は許可されない; 正確なオリジンが必要 |
| リクエストで4xx/5xx | サーバーエラー — CORSの問題ではない |
| 混在コンテンツか | HTTPSページからHTTP — 常にブロックされる |
まとめ
ブラウザのエラーメッセージ、ネットワークタブ、ヘッダーパネルは、ほとんどのCORS問題を5分以内に診断するために必要なすべてを提供します。正確なエラーを読み、OPTIONSリクエストが存在する場合はそれを見つけ、送信されたものと期待されたものを比較し、そこから作業を進めます。やみくもにサーバーにAccess-Control-Allow-Origin: *を追加したい衝動に抵抗してください — まずブラウザが何を求めているかを理解し、正確に応答しましょう。
よくある質問
いいえ。CORSは、サーバーが送信するヘッダーに基づいてブラウザによって強制されます。フロントエンドはこれを上書きできません。サーバーを制御していない場合、正しいヘッダーを追加するバックエンドプロキシを介してリクエストをルーティングできますが、ブラウザ側のコードだけでは制限をバイパスできません。
Postmanはブラウザではなく、同一オリジンポリシーを強制しません。レスポンスのCORSヘッダーをチェックせずに直接リクエストを送信します。ブラウザはユーザーを保護するためにCORSを強制するため、Postmanで成功するリクエストでも、サーバーが必要なレスポンスヘッダーを省略している場合、ブラウザによってブロックされる可能性があります。
プリフライトは、リクエストがGET、HEAD、POST以外のメソッドを使用する場合、カスタムヘッダーを含む場合、またはapplication/x-www-form-urlencoded、multipart/form-data、text/plain以外のContent-Typeを設定する場合にトリガーされます。ブラウザは、サーバーが実際のリクエストを許可することを確認するために、最初にOPTIONSリクエストを送信します。
リクエストにCookieや認証ヘッダーなどの認証情報が含まれている場合、サーバーはワイルドカード値を使用できません。リクエストを行った正確なオリジンで応答し、Access-Control-Allow-Credentialsヘッダーもtrueに設定して含める必要があります。ワイルドカードは認証情報のないリクエストに対してのみ有効です。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.