コンテンツにスキップ

GET に Authorization ヘッダーを追加したら謎の OPTIONS リクエストが出た

原文

You send a simple GET request… No issues.

You add one tiny header: Authorization: Bearer token

Now see extra OPTIONS request ...

Where did this extra request come from… and who asked for it?

要約

Authorization ヘッダーを追加した瞬間に見知らぬ OPTIONS リクエストが発生する。これはブラウザによる CORS Preflight。シンプルリクエストではない条件(カスタムヘッダー等)を満たした場合に、ブラウザが自動で事前確認を行う。

回答

Authorization ヘッダーは CORS の「シンプルリクエスト」条件を満たさないため、ブラウザが Preflight リクエスト(OPTIONS) を自動送信する。サーバーがこの OPTIONS に適切な CORS ヘッダーで応答すれば、元のリクエストが送られる。

解説

CORS とは

CORS(Cross-Origin Resource Sharing) は、異なるオリジン(ドメイン・ポート・プロトコル)へのリクエストを制御するブラウザのセキュリティ機構。

シンプルリクエスト vs Preflight が必要なリクエスト

条件 シンプルリクエスト Preflight 必要
メソッド GET / POST / HEAD PUT / DELETE / PATCH
ヘッダー Accept, Content-Type(一部)のみ Authorization など追加ヘッダー
Content-Type application/x-www-form-urlencoded application/json

Authorization: Bearer token を追加した瞬間 → Preflight が必要になる。

Preflight の流れ

ブラウザ → OPTIONS /api/data HTTP/1.1
           Origin: https://frontend.com
           Access-Control-Request-Method: GET
           Access-Control-Request-Headers: Authorization

サーバー ← HTTP/1.1 204 No Content
           Access-Control-Allow-Origin: https://frontend.com
           Access-Control-Allow-Headers: Authorization
           Access-Control-Allow-Methods: GET, POST
           Access-Control-Max-Age: 86400  ← キャッシュ時間

ブラウザ → GET /api/data (元のリクエスト)
           Authorization: Bearer token

バックエンド側の対応(Go / Gin 例)

// CORSミドルウェア
func CORSMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Header("Access-Control-Allow-Origin", "https://frontend.com")
        c.Header("Access-Control-Allow-Headers", "Authorization, Content-Type")
        c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        c.Header("Access-Control-Max-Age", "86400")

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }
        c.Next()
    }
}

よくあるバグ

  • OPTIONS を 404 にしてしまう → Preflight が失敗してリクエストがブロックされる
  • Access-Control-Max-Age を設定しない → 毎回 Preflight が走りパフォーマンス劣化
  • ワイルドカード * と Authorization の組み合わせcredentials: true の場合、* は使えない

→ 関連: HTTPSとCDNキャッシュHTTPS無音障害

リンク