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無音障害