CORSとは:プリフライト、ヘッダー、CORSエラーの解決方法
CORS解説
CORSとは:プリフライト、ヘッダー、エラーと解決方法 | We.Inc
CORSの仕組みを解説。シンプルリクエストとプリフライトの違い、すべてのAccess-Controlヘッダー、認証情報のルール、よくあるコンソールエラー、Express・Next.js・nginxでのサーバー側の修正方法を紹介します。
CORS(オリジン間リソース共有)は、別オリジンのJavaScriptがレスポンスを読み取ってよいことを、サーバーがブラウザーに伝える仕組みです。サーバーはAccess-Control-Allow-Originと関連ヘッダーで応答します。それ以外のメソッド、カスタムヘッダー、JSONボディを使うリクエストは、先にOPTIONSのプリフライトを送ります。修正は常に呼び出し先のサーバー側で行い、ページ側では行いません。
CORSレスポンスヘッダーの一覧
- Access-Control-Allow-Origin
- レスポンスを読み取れるオリジン(1つのオリジンまたは *)
- Access-Control-Allow-Methods
- プリフライト後に許可されるメソッド
- Access-Control-Allow-Headers
- プリフライト後に許可されるリクエストヘッダー
- Access-Control-Allow-Credentials
- リクエストと一緒に送るCookieと認証情報(値は必ずtrue)
- Access-Control-Expose-Headers
- スクリプトが読み取れるレスポンスヘッダー
- Access-Control-Max-Age
- プリフライトの応答をキャッシュできる秒数
CORSエラーはコードが壊れているように見えますが、実際にはリクエストはたいていサーバーに問題なく届いています。ブラウザーが応答をスクリプトに渡すのを拒否しただけです。下のシミュレーターでリクエストとサーバーの設定を指定すると、プリフライトが発生するか、ブラウザーがレスポンスを通すか、どのコンソールメッセージが表示されるかがわかります。
- ページのオリジン、メソッド、カスタムヘッダー、Cookieを送るかどうかを設定します。
- サーバーの応答内容を設定します:許可するオリジン、メソッド、ヘッダー、認証情報フラグです。
- 判定を確認します:プリフライトが必要か、通過するか、失敗した場合のコンソール上の理由がわかります。
- 下にある対応するサーバー用スニペットをコピーし、フロントエンドではなくAPIに追加します。
- Cookieを扱う場合は、許可リストからリクエスト元の正確なオリジンを返しましょう。認証情報付きではワイルドカードが拒否されるためです。
- 許可するオリジンがリクエストごとに変わる場合は、Vary: Originを追加しましょう。キャッシュがあるサイト向けの応答を別のサイトに返さなくなります。
- OPTIONSには2xxステータスで、リダイレクトなしで応答しましょう。ログインへのリダイレクトに当たるプリフライトは必ず失敗します。
- 自分のブラウザーでCORSを無効にしても、自分のマシンで問題が見えなくなるだけです。訪問者には引き続きエラーが出ます。
- サーバー間の通信にはCORSは関係しません。APIを管理できない場合は、代わりに自分のバックエンドから呼び出しましょう。
よくある質問
ブラウザーでは失敗するのに、Postmanやcurlでは動くのはなぜですか?
CORSはブラウザーだけが適用する仕組みで、あるサイトのスクリプトがあなたのCookieを使って別のサイトのデータを読むのを防ぎます。curlのようなツールは信頼できないページのコードを実行していないので、CORSを無視します。サーバーのレスポンスは同じで、ブラウザーだけがそれを渡さないと判断したのです。
プリフライトリクエストが発生する条件は何ですか?
GET、HEAD、POST以外のメソッド、安全とされる少数のヘッダー(Accept、Accept-Language、Content-Language、Content-Type、Range)以外のヘッダー、またはフォームエンコード、multipart、text/plain以外のContent-Typeです。最もよくある原因はapplication/jsonの送信です。
フロントエンドでCORSを解決できますか?
できません。リクエストにAccess-Controlヘッダーを付けても効果はなく、mode: no-corsではスクリプトが読めない不透明なレスポンスが返ります。APIにヘッダーを追加してもらうか、自分のサーバーでデータを取得して中継するかのどちらかです。
CookieとAccess-Control-Allow-Origin: * を一緒に使えますか?
使えません。リクエストが認証情報を含む場合、ブラウザーはワイルドカードのオリジンを拒否し、正確なオリジンとAccess-Control-Allow-Credentials: trueを求めます。メソッド、ヘッダー、公開ヘッダーのリストでのワイルドカードも同様です。
スクリプトでカスタムレスポンスヘッダーを読めないのはなぜですか?
クロスオリジンのスクリプトは、デフォルトでは安全なレスポンスヘッダーの短いリストしか見られません。それ以外のヘッダー(たとえばX-Request-Id)をAccess-Control-Expose-Headersに列挙すると、読み取れるようになります。
同一オリジンと、ブラウザーが読み取りをブロックする理由
- オリジンとは、スキーム、ホスト、ポートの組み合わせです。https://app.example.comとhttps://api.example.comは別のオリジンで、同じドメインのhttp版とhttps版も別のオリジンです。デフォルトでは、スクリプトは別オリジンにリクエストを送れても、返ってきた内容は読めません。
- CORSはそれを許可するための仕組みです。APIが信頼するオリジンを列挙し、ブラウザーが訪問者に代わってそのリストを適用します。だからこそ、ヘッダーは呼び出し先のサーバーから返す必要があります。
シンプルリクエストとプリフライト
- 単純なGETや通常のフォームからのPOSTは、Originヘッダー付きでそのまま送信されます。ブラウザーはその後、応答に許可されたオリジンがあるかを確認してから、コードに渡します。
- それ以外のリクエストは、先にAccess-Control-Request-MethodとAccess-Control-Request-Headersを含むOPTIONSリクエストを送ります。サーバーが両方を承認した場合にのみ、本来のリクエストが送信されます。Access-Control-Max-Ageでブラウザーに承認を記憶させられますが、ブラウザーごとに保持期間の上限があります。
コンソールエラーの読み方
- セキュリティ上の理由で、JavaScriptにはfetchが失敗したことだけが、詳細のないTypeErrorとして伝わります。本当の理由はブラウザーのコンソールに表示され、欠けているか誤っているヘッダーの名前が示されます。下のメッセージ一覧と照らし合わせてください。
よくあるCORSエラーとその解決方法
メッセージの文言はブラウザーによって異なります。以下はMDNでFirefoxが示す理由で、Chromeは同じ内容をより長い文で表示します。
CORSのコンソール上の理由とサーバー側の修正
- CORS header 'Access-Control-Allow-Origin' missing
- レスポンスにallow-originヘッダーがまったくない
- エラーを含むすべてのレスポンスにヘッダーを追加する
- CORS header 'Access-Control-Allow-Origin' does not match
- ヘッダーが別のオリジンを指定している
- 許可リストから呼び出し元のオリジンを返す
- Credential is not supported if the CORS header 'Access-Control-Allow-Origin' is '*'
- Cookieが送られたのに、サーバーが * で応答した
- 正確なオリジンとAllow-Credentials: trueを返す
- expected 'true' in CORS header 'Access-Control-Allow-Credentials'
- 認証情報が送られたのに、フラグがない
- Access-Control-Allow-Credentials: trueを送る
- Did not find method in CORS header 'Access-Control-Allow-Methods'
- プリフライトでメソッドが承認されなかった
- Allow-MethodsにPUT、PATCH、DELETEを列挙する
- missing token in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel
- Authorizationなどのリクエストヘッダーが承認されなかった
- Allow-Headersに列挙する
- CORS preflight channel did not succeed
- OPTIONSが失敗した、リダイレクトされた、またはエラーを返した
- OPTIONSに204とヘッダーで応答する
- Multiple CORS header 'Access-Control-Allow-Origin' not allowed
- 2つの層(アプリとプロキシ)の両方がヘッダーを追加した
- 1か所だけで設定する
サーバー用スニペット
Express:認証情報付きの許可リスト
オリジンがリストにある場合だけそれを返し、プリフライトには204で応答し、Varyを追加してキャッシュがオリジンを区別できるようにします。
Next.jsのルートハンドラー
Cookieを使わない公開エンドポイントならワイルドカードを使えます。OPTIONSをエクスポートして、プリフライトに専用の応答を返します。
APIの前段に置いたnginx
alwaysフラグでエラーレスポンスにもヘッダーが付くので、500でもCORSエラーではなく本来のステータスが表示されます。
ブラウザー側:クロスオリジンでCookieを送る
credentials: "include" を指定すると、サーバー側でより厳しい認証情報のルールが有効になります。
CORSリクエストシミュレーター
リクエストとサーバーのCORSヘッダーを指定すると、プリフライトが送られるか、ブラウザーがスクリプトにレスポンスの読み取りを許すかがわかります。
リクエスト
サーバーの応答
ページのオリジン
APIのオリジン
メソッド
Content-Type
ボディなし
追加のリクエストヘッダー(なしの場合は空欄)
Cookieを送信(credentials: include)
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Credentials: true
判定
同一オリジン:CORSは適用されず、レスポンスを読み取れます。
プリフライトなし:これはシンプルリクエストです。
プリフライトが必要:ブラウザーは先にOPTIONSを送ります。
理由:メソッドがGET、HEAD、POSTのいずれでもありません。
理由:ヘッダー {v} は安全リストに含まれていません。
理由:Content-Type {v} は3つのシンプルな型のいずれでもありません。
許可:スクリプトはレスポンスを読み取れます。
ブロック。コンソール上の理由:
サーバーが送るべきヘッダー
無料で始める · クレジットカード不要
関連: ガイド|Webマーケティングと集客を学ぶ
製品
ご利用いただける方
機能
リソース
会社情報
サイトマップを見る