Preflight リクエストとは何か
私たちは一般的にブラウザでよく使用されるリクエストとして、POST、GET、PUT、DELETE などを知っていますが、「OPTIONS」というリクエストタイプがあることに気づいたことはありますか?
通常、「preflight(プリフライト)リクエスト」とは、OPTIONS リクエストのことを指します。ブラウザがこれから実行しようとするリクエストがサーバーに予期しない影響を与える可能性があると判断した場合、ブラウザは自動的に OPTIONS リクエストを送信します。このプリフライトリクエストを通じて、ブラウザはサーバーが当該リクエストを許可するかどうかを確認し、許可された場合にのみ、実際のリクエストを実行します。
通常、preflight リクエストはユーザーが自分で管理や操作を行う必要はなく、ブラウザとサーバーによって自動的に処理されます。
リクエストは一般的に以下のような形式になります:
Access-Control-Request-Headers: x-requested-with
Access-Control-Request-Method: POST
Origin: http://preflight.example.com
ここで重要なのは以下の 3 つのフィールドです:
-
Origin
: アクセス元の情報 -
Access-Control-Request-Method
: 実際のリクエストのメソッド -
Access-Control-Request-Headers
: 実際のリクエストのリクエストヘッダー
レスポンスは以下のようになります:
Access-Control-Allow-Headers: Content-Type, Content-Length, Authorization, Accept, X-Requested-With
Access-Control-Allow-Origin: http://preflight.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
このレスポンスの中で注目すべきは以下の 3 つのフィールドです:
-
Access-Control-Allow-Origin
: 許可されているアクセス元 -
Access-Control-Allow-Headers
: 許可されているカスタムヘッダー -
Access-Control-Allow-Methods
: 許可されている HTTP メソッド
これらのどれかが許可されていない場合、ブラウザは実際のリクエストを実行せず、自動的に CORS エラーを発生させます。
また、Access-Control-Max-Age
は、このプリフライトリクエストの有効期間を示しており、この時間内であればブラウザは同じリクエストのプリフライトを再度実行しません。
それでは、どのような場合に preflight リクエストが発生するのでしょうか?
Preflight リクエストが発生する条件
preflight リクエストは CORS(Cross-Origin Resource Sharing)の仕様の一部であり、すべての最新ブラウザはこの仕様を実装しています。ただし、一部のブラウザでは仕様が拡張されています。
MDN によると、以下の 5 つの条件をすべて満たす場合にのみ、preflight リクエストは発生せず、それ以外の場合は、実際のリクエストを実行する前に preflight リクエストが送信されます。
以下の 5 つの条件のうち、1 つでも満たさない場合、preflight リクエストが発生します:
-
リクエストメソッドの制限:使用できるメソッドは
GET
、POST
、HEAD
のみ -
リクエストヘッダーの制限:使用できるヘッダーは以下の 9 種類のみ
Accept
、Accept-Language
、Content-Language
、Content-Type
、DPR
、Downlink
、Save-Data
、Viewport-Width
、Width
-
Content-Type の制限:
Content-Type
は以下の 3 種類のいずれかであること
text/plain
、multipart/form-data
、application/x-www-form-urlencoded
-
XMLHttpRequestUpload オブジェクトの制限:
XMLHttpRequestUpload
オブジェクトにイベントリスナーを登録していないこと -
ReadableStream オブジェクトの制限:リクエスト内で
ReadableStream
オブジェクトを使用しないこと
開発の現場では、主に上記の最初の 3 つの制限が影響を及ぼします。
特に、カスタムリクエストヘッダーを設定した場合や、Content-Type
が制限された種類以外の場合に、preflight リクエストが発生しやすくなります。
Preflight リクエストの目的
ここまでで、preflight リクエストが何か、どのような条件で発生するかを理解しました。
では、この preflight リクエストはどのような目的で設計され、どのように問題を回避する役割を果たしているのでしょうか?
この話をするには、CORS(クロスオリジンリソース共有)の概念に触れる必要があります。
preflight リクエストは CORS の一部として設計されており、クロスオリジン(異なるドメイン間)でのリクエストを制限することで、Web のセキュリティを大幅に向上させる役割を持っています。
もし CORS が存在しなかった場合、特別な設定なしにすべてのウェブサイトのリソースが自由に共有されることになります。例えば、A サイトのスクリプトが B サイトの Cookie などの機密情報にアクセスできる状態になり、逆も同様です。
しかし、CORS を導入することで、デフォルトではそのようなアクセスが禁止され、適切な設定を行わない限り、異なるオリジン間でのリクエストができなくなります。
CORS の詳細なセキュリティメカニズムは複雑ですが、詳しく知りたい方は MDN のドキュメントを参照してください。
Preflight リクエストを正しく処理する方法
サーバーサイドの開発者にとって、preflight リクエストが発生する可能性のあるリクエストをどのように適切に処理すればよいでしょうか?
基本的に、以下の 3 つのヘッダーを適切に設定することが重要です:
-
Access-Control-Allow-Origin
:許可するオリジン(サイト)を設定 -
Access-Control-Allow-Headers
:許可するカスタムリクエストヘッダーを設定 -
Access-Control-Allow-Methods
:許可するリクエストメソッドを設定
推奨設定:
ctx.set('Access-Control-Allow-Origin', 許可するサイトのドメイン);
ctx.set('Access-Control-Allow-Credentials', true);
ctx.set('Access-Control-Max-Age', 86400000);
ctx.set('Access-Control-Allow-Methods', 'OPTIONS, HEAD, 実際に許可するメソッド');
ctx.set(
'Access-Control-Allow-Headers',
'Content-Type, Content-Length, Authorization, Accept, X-Requested-With'
);
また、OPTIONS
メソッドのレスポンスコードを適切に設定する必要があります。
if (ctx.request.method === 'OPTIONS') {
ctx.response.status = 204;
}
このように設定することで、ブラウザが正しく preflight リクエストを処理できるようになります。
Preflight リクエストと CORS の関係
MDN の説明によると、preflight リクエストは CORS 仕様の一部であり、クロスオリジン(異なるドメイン間)でのリクエストに関連する機能です。そのため、リクエストがクロスオリジンでない場合、たとえ preflight リクエストの発生条件(5 つのルール)に当てはまらなくても、preflight リクエストは発生しません。
つまり、クロスオリジンであっても必ず preflight リクエストが発生するわけではないが、preflight リクエストが発生した場合は必ずクロスオリジンのリクエストである ということです。
この仕組みは直感的にも理解しやすいでしょう。
CORS はクロスオリジン環境におけるセキュリティを強化するための仕組みの一つであり、同一オリジン内でのリクエストには適用されません。
同じオリジン(ドメイン)内であれば、サーバーサイドの開発者とフロントエンドの開発者は通常、十分な共通認識を持って開発を行うため、CORS の制限が不要になるからです。
このように、セキュリティと利便性のバランスをとるために、CORS では preflight リクエストを条件付きで適用する仕様になっています。
私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。
Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:
複数言語サポート
- Node.js、Python、Go、Rustで開発できます。
無制限のプロジェクトデプロイ
- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。
比類のないコスト効率
- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。
洗練された開発者体験
- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。
簡単なスケーラビリティと高パフォーマンス
- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。
Xでフォローする:@LeapcellHQ