🌍 CORS テスト
URL とリクエスト元 Origin を指定して CORS プリフライト (OPTIONS) と実リクエストを送信し、Access-Control-Allow-Origin / Methods / Headers / Credentials / Max-Age を診断します。
📚 CORS の基本
• ブラウザは「異なるオリジンへの fetch / XHR」をブロックします。これがクロスオリジン制限です。
• CORS はサーバーが Access-Control-Allow-Origin ヘッダで「許可するオリジン」を宣言する仕組みです。
• プリフライトとは、ブラウザが本番リクエストの前に送信する OPTIONS リクエストです。Custom Header や非単純メソッド (PUT, DELETE 等) で発生します。
• Access-Control-Allow-Credentials: true と Access-Control-Allow-Origin: * は併用できません。
📖 この診断で分かること
この診断は Origin ヘッダーを付けたリクエストとプリフライトの OPTIONS を実際に送り、返ってくる Access-Control-* ヘッダーを確認します。前提として、CORS を強制しているのはブラウザだけです。curl やサーバー間通信は CORS を一切見ません。つまり CORS は API を守る仕組みではなく、利用者のブラウザに保存された資格情報が、悪意あるサイトから勝手に使われるのを防ぐ仕組みです。
| 判定項目 | 何を見ているか | 引っかかったときの対処 |
|---|---|---|
| 複数のオリジンを列挙している | Access-Control-Allow-Origin に書けるのは1 つのオリジンか * だけです。カンマ区切りで複数並べたヘッダーは仕様上不正で、ブラウザは丸ごと拒否します。「設定したはずなのに CORS エラーになる」原因として非常に多い書き方です。 |
サーバー側でリクエストの Origin をホワイトリストと照合し、一致したその 1 つだけをそのまま返します。このときレスポンスは Origin ごとに内容が変わるので、Vary: Origin を必ず付けてください。付け忘れると CDN が最初の 1 件をキャッシュし、別オリジン向けの応答を配ってしまいます。 |
| プリフライトが毎回飛んでいる | Content-Type: application/json を送る、Authorization や独自ヘッダーを付ける、GET / POST / HEAD 以外のメソッドを使う — このいずれかで単純リクエストではなくなり、本番リクエストの前に OPTIONS の往復が 1 回増えます。API を叩くたびに往復が倍になるため、レイテンシの高い回線では体感に直結します。 |
Access-Control-Allow-Headers に実際に送るヘッダーを漏れなく列挙し(ここに無いヘッダーが 1 つでもあるとプリフライトが失敗します)、Access-Control-Max-Age でプリフライト結果をキャッシュさせます。Chrome の上限は 7200 秒なので、それ以上を指定しても切り捨てられます。 |
| エラー応答に CORS ヘッダーが無い | 多くのフレームワークは、例外が起きて 500 を返すときにミドルウェアを通らず、Access-Control-Allow-Origin が付かないまま応答します。するとブラウザは中身を読ませてくれないので、開発者コンソールには「CORS エラー」としか出ません。本当の原因であるサーバー側の例外が完全に隠れる、非常に厄介な状態です。 |
例外ハンドラや error_page の応答にも同じ CORS ヘッダーを付けてください。切り分けの手順としては、開発者ツールの Network タブで実際のステータスコードを見るのが先です。ここが 500 や 404 なら CORS の設定は無関係で、直すべきはアプリ側です。 |
CORS は認可ではありません。Access-Control-Allow-Origin: * を付けても付けなくても、API そのものは誰でも curl で叩けます。守るべきデータがあるならトークン認証やセッション検証を実装してください。もう一つの誤解は「CORS を設定すれば CSRF も防げる」というもので、これは成り立ちません。<form> の送信や <img> の読み込みはそもそも CORS の管轄外だからです。CSRF には SameSite Cookie と CSRF トークンが別途必要です。