Interactive
CORSプリフライト判定可視化
「いつOPTIONSが飛ぶのか」は気分ではなく、Fetch標準の厳密な判定で決まっています。OPTIONSは飛ぶかタブではメソッド・ヘッダ・Content-Typeを実際に判定にかけ、どの条件で発火したかを示します。サーバー応答との照合タブではAccess-Control-*を編集して、どの段で照合が落ちたかを確認できます。
シナリオ
記事の「GETは通るのにPOSTだけ落ちる」の正体。メソッドは safelisted なのに Content-Type だけで発火する。
リクエストヘッダ
✗ application/json は許可された3種のいずれでもない
safelistedヘッダの値の合計: 0 / 1024 バイト
OPTIONS が先行する(プリフライトあり)
本番リクエストはOPTIONSが許可されるまで一度も送られない。
判定の内訳
https://app.example.com → https://api.example.com で別オリジン。CORSの判定対象。
POST は safelisted method
content-type が safelisted でない
Content-Type が safelisted になるのはapplication/x-www-form-urlencodedmultipart/form-datatext/plainの3種のときだけ。値の長さも1つあたり128バイトまで。
ブラウザが送るOPTIONS
OPTIONS /items HTTP/1.1 Origin: https://app.example.com Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type
申告するヘッダ名は小文字に正規化され、ソートされて送られる(記述順は影響しない)。
判定は Fetch 標準の手続きをそのまま実装したもので、ブラウザ実装ごとの差異(max-ageの上限など)は明示している箇所のみ反映しています。
ここが分かる
- POSTは常にプリフライトされる、は誤り——POST自体はsafelistedなメソッドで、発火させているのは
Content-Type: application/jsonのほう。同じPOSTでもtext/plainに変えるとOPTIONSが消えるのを実際に確認できる。 - 資格情報付きでは
*が記号になる——Allow-OriginだけでなくAllow-MethodsもAllow-Headersも同時にワイルドカードの意味を失う。credentialsを切り替えると、同じサーバー応答が成功と失敗を行き来する。 AuthorizationはAllow-Headers: *では絶対に通らない——資格情報を送っていなくても、この1つだけは名前を明示する必要がある(non-wildcardヘッダ)。同じ*でX-Trace-Idは通るのと対比できる。- プリフライトが通っても「メソッドが許可された」とは限らない——GET/HEAD/POSTはsafelistedなので、
Allow-Methodsに書かれていなくてもメソッドの照合自体は通る。Allow-Methods: GETだけの応答でJSONのPOSTが通ってしまう様子を確認できる。 - キャッシュキーはオリジン・URL・メソッド・申告ヘッダ集合・資格情報モードの組。独自ヘッダを1つ足しただけでOPTIONSが復活する理由が、キーの変化として見える。