プリフライトリクエストとは?送信条件などをわかりやすく解説!

この記事では『プリフライトリクエスト(OPTIONSメソッドのリクエスト)』について、以下の内容を図を用いてわかりやすく解説します。

  • プリフライトリクエストとは
  • プリフライトリクエストに含まれるヘッダー
  • プリフライトリクエストが送信されない条件
  • プリフライトリクエストが送信される例
  • プリフライトリクエストを減らす方法

プリフライトリクエストとは

プリフライトリクエストとは

ブラウザがGET, POSTDELETEなどのリクエストを送る際、実際のリクエストを送信する前にOPTIONSリクエストが事前に送られることがあります。

これはプリフライトリクエストと呼ばれ、CORS(Cross-Origin Resource Sharing)の仕組みの一部として動作します。プリフライトリクエストが成功しないと、実際のリクエスト(GET, POSTDELETEなど)は送信されません。

プリフライトリクエストは、クロスオリジンリクエストの安全性を確認するために送信されます。ブラウザは、サーバーに対して「このリクエストを送っても大丈夫?」とプリフライトリクエスト(OPTIONSメソッドのリクエスト)を送信して事前に確認することで、セキュリティリスクを減らします。

preflightという言葉の通り、本番前のテスト飛行のようなイメージです。

プリフライトリクエストに含まれるヘッダー

プリフライトリクエストには、以下の3つのヘッダーを含みます。

  • Access-Control-Request-Method
    • 実際のリクエストで使用予定のHTTPメソッド(例: POST, DELETE など)をサーバーへ通知するヘッダー
  • 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ヘッダーのみを使用
    • ブラウザが自動で追加するヘッダー(たとえば ConnectionUser-Agent
    • Accept
    • Accept-Language
    • Content-Language(以下のいずれかに制限)
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • Rangebytes=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/jsonAuthorizationというカスタムヘッダーを含んでいるため、ブラウザはプリフライトリクエストを先に送信します。

プリフライトリクエストを減らす方法

プリフライトリクエストを完全になくすことはできませんが、その発生頻度を抑える方法はいくつかあります。以下に代表的な方法を紹介します。

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-Typeapplication/jsonではなくtext/plainにする
  • POST以外のメソッドでデータを送る

ただし、セキュリティやAPI設計とのバランスを考慮する必要があります。