【テクニカル・上級編】 CORS(Cross-Origin Resource Sharing)のプリフライトリクエスト(OPTIONSメソッド) – Web APIアーキテクチャ・データ連携実践ガイド

CORSのプリフライトリクエストを「ただの制約」と呼ぶのはもうやめよう

モダンなWebアプリケーション開発において、CORS (Cross-Origin Resource Sharing) は避けて通れない関門だ。しかし、多くの開発者はこれを「ブラウザが勝手に送ってくる謎の OPTIONS リクエスト」程度に捉え、パフォーマンスへの影響を軽視している。

ネットワークエンジニアの視点から言えば、この OPTIONS メソッドによるプリフライトリクエストこそ、遅延(Latency)を極限まで削るべきプロトコルの最前線だ。なぜなら、これは本丸の GET や POST に到達する前に発生する、いわば「強制的なRTT(Round Trip Time)の浪費」だからだ。

1. プリフライトリクエストの解剖:なぜパケットを無駄にするのか

ブラウザが OPTIONS を投げるのは、サーバーが「そのリクエストを許可するか」を事前に確認するためだ。しかし、この挙動を放置すれば、高レイテンシな環境ではユーザー体験が致命的に損なわれる。

例えば、モバイルネットワーク環境下で 3G/4G/5G のハンドシェイクと TLS のネゴシエーションを2回繰り返すコストを考えてみてほしい。

# ブラウザが送信するプリフライトのパケット(抜粋)
OPTIONS /api/v1/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

サーバー側でこの OPTIONS を受ける際、多くの実装が「アプリケーション層までリクエストを回している」。これはインフラアーキテクトとしては失格だ。この処理はWebサーバー(Nginx等)のレイヤーで即座に完結させなければならない。

2. パフォーマンスの最適化:RTTをどう削減するか

このプリフライトを「不要な往復」にしないための定石は、Access-Control-Max-Age を活用することだ。

# Nginxの設定例:プリフライトのキャッシュ時間を最大化する
location /api/ {
    if ($request_method = 'OPTIONS') {
        # 86400秒(24時間)キャッシュさせて、後続のリクエストでプリフライトをスキップさせる
        add_header 'Access-Control-Max-Age' 86400;
        add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204; # 処理をここで打ち切り、アプリサーバーへ回さない
    }
}

この設定により、一度プリフライトが成功すれば、ブラウザは一定期間 OPTIONS を飛ばさなくなる。これだけで、1回のRTTを完全に削減できる。もし TLS 1.3 を導入していれば、0-RTTの恩恵と相まって、パフォーマンスは劇的に向上するはずだ。

3. セキュリティとネットワークの境界線

時折、「Access-Control-Allow-Origin: * を指定すれば万事解決」という危険な設計を見かける。これは Credential を伴うリクエスト(CookieやAuthorizationヘッダー)が絡む瞬間に破綻する脆弱性の温床だ。

さらに、Vary ヘッダーの扱いに注意してほしい。キャッシュサーバーやCDNが Origin ヘッダーを正しく識別できない場合、誤ったキャッシュを返却し、意図しないクロスオリジンアクセスを許可してしまう可能性がある。

# VaryヘッダーでOriginを識別させる重要設定
add_header 'Vary' 'Origin';

4. TCP/TLSチューニングの観点から

プリフライトが頻発する環境では、サーバーの TCP Keep-Alive や TLS Session Resumption の設定が効いてくる。

  • TCPバッファ: net.ipv4.tcp_rmem や tcp_wmem を調整し、小さな OPTIONS パケットがウィンドウサイズ制限で滞留しないようにする。
  • TLSハンドシェイクの最適化: TLS False Start を有効にし、クライアント側でハンドシェイク完了を待たずにアプリケーションデータを送り出せるようにする。

結論:プロトコルを愛する者へ

CORS は単なる「ブラウザのセキュリティ機能」ではない。それはネットワークという物理的な制限の中で、リクエストの正当性を担保しつつ、いかに効率よくペイロードを届けるかという「通信の設計図」そのものだ。

現場で tcpdump を走らせ、OPTIONS が無駄にネットワークを往復しているのを見つけたら、それはあなたのインフラを最適化する絶好のチャンスだと思ってほしい。プロトコルの深淵を覗くことは、エンドユーザーのストレスを1ミリ秒でも減らすことに繋がる。

次回のデプロイ時には、ぜひ Access-Control-Max-Age の値をもう一度見直してみてほしい。たった一行のヘッダーが、あなたのアプリケーションのレスポンスを劇的に変えるはずだ。

コメント

タイトルとURLをコピーしました