Preflight request は、CORS の通信でブラウザが本番リクエストの前に自動で送る確認用の OPTIONS リクエストです。
「これからこういうメソッドとヘッダーで送りたいが、受け入れるか?」をサーバーに先に聞き、OKの応答が返って初めて本体のリクエストが飛びます。
いつ発生するか(正確な条件)
すべてのCORSリクエストで飛ぶわけではありません。「単純リクエスト」の条件から外れたときに発生します。
- メソッドが
GET/HEAD/POST以外(PUTPATCHDELETEなど) Authorizationや独自ヘッダー(X-API-Keyなど)を付けたContent-Typeがフォーム系3種(text/plain/multipart/form-data/application/x-www-form-urlencoded)以外——つまりapplication/jsonを送ると必ず preflight が飛ぶ
現代のAPI呼び出し(JSON+認証ヘッダー)はほぼ確実に該当します。
やり取りの中身
OPTIONS /api/users
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-type
Origin: https://app.example.com
サーバーが Access-Control-Allow-Origin / Allow-Methods / Allow-Headers で応答し、要求が許可範囲に収まっていれば本体が送られます。
なお Access-Control-Max-Age で preflight の結果をブラウザにキャッシュさせられ、毎回の往復を減らせます。
デバッグの定石
「ブラウザからだけ POST が失敗する」の多くは本体ではなく preflight の失敗です。
- 開発者ツールの Network タブで
OPTIONSの行を探す - その応答コードと
Access-Control-Allow--*ヘッダーを見る(APIがOPTIONSに 404/405 を返している、が定番原因) curlでは再現しない(curl は preflight を送らない)——これも preflight を疑うサイン
近い用語との違い
- CORS … 仕組み全体。preflight はその一部
- Same-Origin Policy … そもそもの制約。CORS が門で、preflight は門番への事前確認
詰まったときの手順は CORSのよくあるハマりどころとデバッグ手順 で扱っています。