この記事では『プリフライトリクエスト(OPTIONSメソッドのリクエスト)』について、以下の内容を図を用いてわかりやすく解説します。
- プリフライトリクエストとは
- プリフライトリクエストに含まれるヘッダー
- プリフライトリクエストが送信されない条件
- プリフライトリクエストが送信される例
- プリフライトリクエストを減らす方法
プリフライトリクエストとは

ブラウザがGET
, POST
, DELETE
などのリクエストを送る際、実際のリクエストを送信する前にOPTIONS
リクエストが事前に送られることがあります。
これはプリフライトリクエストと呼ばれ、CORS(Cross-Origin Resource Sharing)の仕組みの一部として動作します。プリフライトリクエストが成功しないと、実際のリクエスト(GET
, POST
, DELETE
など)は送信されません。
プリフライトリクエストは、クロスオリジンリクエストの安全性を確認するために送信されます。ブラウザは、サーバーに対して「このリクエストを送っても大丈夫?」とプリフライトリクエスト(OPTIONS
メソッドのリクエスト)を送信して事前に確認することで、セキュリティリスクを減らします。
preflight
という言葉の通り、本番前のテスト飛行のようなイメージです。
プリフライトリクエストに含まれるヘッダー
プリフライトリクエストには、以下の3つのヘッダーを含みます。
Access-Control-Request-Method
- 実際のリクエストで使用予定のHTTPメソッド(例:
POST
,DELETE
など)をサーバーへ通知するヘッダー
- 実際のリクエストで使用予定のHTTPメソッド(例:
Access-Control-Request-Headers
- 実際のリクエストで使用予定のカスタムヘッダー(例:
Authorization
など)をサーバーへ通知するヘッダー
- 実際のリクエストで使用予定のカスタムヘッダー(例:
Origin
- リクエストの発信元のオリジン(例:
https://example.com
)を示すヘッダー
- リクエストの発信元のオリジン(例:
プリフライトリクエストの一例を以下に示します。
プリフライトリクエストの例
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization, Content-Type
Origin: https://example.com
サーバーのレスポンス
サーバーがリクエストを許可すると、以下のようなレスポンスを返します。
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
Access-Control-Allow-Methodsヘッダーにより、DELETE
メソッドが許可されていることが確認できます。Access-Control-Allow-Headers
ヘッダーによりAuthorization
ヘッダーを許可していることが確認できます。許可されれば、次に実際のリクエスト(DELETE
) が送信されます。
プリフライトリクエストが送信されない条件
リクエストの種類によってはプリフライトリクエストは送信されません。プリフライトリクエストが送信されるかどうかは、リクエストが「単純リクエスト(Simple Request)」に該当するかどうかで決まります。
単純リクエストの条件を以下に示します。以下の条件をすべて満たす場合は、プリフライトリクエストは送信されません。
単純リクエストの条件
- 許可されたHTTPメソッドのみを使用
GET
HEAD
POST
- 許可されたHTTPヘッダーのみを使用
- ブラウザが自動で追加するヘッダー(たとえば
Connection
、User-Agent
) Accept
Accept-Language
Content-Language
(以下のいずれかに制限)application/x-www-form-urlencoded
multipart/form-data
text/plain
Range
(bytes=256-
やbytes=127-255
のような範囲指定)
- ブラウザが自動で追加するヘッダー(たとえば
- リクエストボディが特定の形式
ReadableStream
オブジェクトを含まないXMLHttpRequestUpload
にイベントリスナーが登録されていない
プリフライトリクエストが送信される例
例えば、以下のようなリクエストは単純リクエストの条件を満たさないためプリフライトリクエストが送信されます。
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // NG: 単純リクエストの条件外
'Authorization': 'Bearer token123' // NG: カスタムヘッダー
},
body: JSON.stringify({ name: 'John' })
});
このリクエストでは、Content-Type: application/json
やAuthorization
というカスタムヘッダーを含んでいるため、ブラウザはプリフライトリクエストを先に送信します。
プリフライトリクエストを減らす方法
プリフライトリクエストを完全になくすことはできませんが、その発生頻度を抑える方法はいくつかあります。以下に代表的な方法を紹介します。
Access-Control-Max-Ageを設定する
サーバー側でAccess-Control-Max-Age
ヘッダーを設定すると、ブラウザがプリフライトリクエストの結果をキャッシュし、一定時間は再送しなくなります。
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
この設定により、同じリクエストであれば最大24時間(86400秒)プリフライトリクエストなしで実際のリクエストを送信できます。
単純リクエストを意識する
可能な場合は、単純リクエストの条件に合わせてリクエストを設計すると、プリフライトリクエストを回避できます。
- カスタムヘッダーを極力使わない
Content-Type
をapplication/json
ではなくtext/plain
にするPOST
以外のメソッドでデータを送る
ただし、セキュリティやAPI設計とのバランスを考慮する必要があります。