サーバーでOPTIONSプリフライトリクエストを処理する

サーバーがOPTIONSプリフライトリクエストに正しく応答することを確認します。405エラー、欠落ヘッダー、フレームワーク固有の処理のよくある落とし穴を解説します。

Preflight

詳細な説明

OPTIONSリクエストへの応答

サーバーがOPTIONSリクエストに対して405 Method Not AllowedやHTMLボディ付きの200を返す場合、ブラウザはプリフライトに失敗し実際のリクエストをブロックします。サーバーは204 No Content(または200)と正しいCORSヘッダーで応答する必要があります。

ブラウザが送信する内容

OPTIONS /api/users HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

サーバーが返すべき内容

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Vary: Origin

よくある落とし穴

  1. フレームワークのルーティングがOPTIONSを無視 — 多くのフレームワークはデフォルトでGET/POSTのみルーティングします。OPTIONSを明示的に処理するか、CORSミドルウェアを使用する必要があります。
  2. 認証ミドルウェアが先に実行される — 認証レイヤーがCORSヘッダー設定前に未認証リクエストを拒否すると、プリフライトが401で失敗します。CORSミドルウェアは認証の前に実行する必要があります。
  3. リバースプロキシがヘッダーを除去 — 設定ミスのNginxやAWS ALBがAccess-Control-*ヘッダーを除去する場合があります。Nginxではalwaysを追加して非2xxレスポンスでもヘッダーを送信します。
  4. Varyヘッダーの欠落Vary: Originがないと、キャッシュされたプリフライトレスポンスがオリジン間で漏洩する可能性があります。

Express.js: 認証前のCORS

// CORSは認証ミドルウェアの前に配置
app.use(cors(corsOptions));
app.use(authMiddleware);

Nginx: 明示的なOPTIONSブロック

if ($request_method = 'OPTIONS') {
    add_header Access-Control-Allow-Origin $cors_origin always;
    add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
    add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
    add_header Access-Control-Max-Age 3600 always;
    return 204;
}

ユースケース

バックエンド開発者が新しいAPIエンドポイントをデプロイし、GETリクエストは動作するのにPUTとDELETEリクエストがCORSエラーで失敗することを発見しました。非シンプルメソッドがプリフライトをトリガーし、サーバーがOPTIONSを処理するよう設定されていなかったことが原因です。

試してみる — CORS Header Builder

フルツールを開く