Accept ヘッダーの RFC 9110 準拠をオプトイン化する strict_accept_header 設定の追加
Rails 8.2 では config.action_dispatch.strict_accept_header フラグが導入され、Accept ヘッダーに */* が含まれていても RFC 9110 に従ったコンテンツネゴシエーションを選択的に有効化できます。デフォルトは既存アプリへの影響を防ぐため false で、load_defaults 8.2 を利用した新規プロジェクトだけが自動で true になります。
背景
従来の Rails は */* を含む Accept ヘッダーを ブラウザ判定 とみなし、常に HTML を返すロジック(BROWSER_LIKE_ACCEPTS)を実装していました。これは 2010 年当時の IE7 が Accept ヘッダーを正しく解析できなかったことへの回避策です。
近年の主要ブラウザは RFC 9110 が定めるメディアタイプの優先順位を正しく処理できるため、Accept: application/json, */* で JSON が返されるべきですが、Rails の古い挙動がそれを妨げていました。Issue #57353 でこの不整合が指摘され、削除すべきワークアラウンドとして認識されました。
この挙動をそのまま変更すると既存アプリが予期せず HTML から別形式に切り替わるリスクがあるため、オプトイン方式で新しい振る舞いを導入する方針が採られました。
技術的な変更
actionpack/lib/action_dispatch/http/mime_negotiation.rb に strict_accept_header 用のクラス属性アクセサが追加され、valid_accept_header の判定に次のロジックが組み込まれました。
@@
- def valid_accept_header
- (xhr? && (accept.present? || content_mime_type)) ||
- (accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS))
- end
+ def valid_accept_header
+ if xhr?
+ accept.present? || content_mime_type
+ elsif accept.present?
+ self.class.strict_accept_header || !accept.match?(BROWSER_LIKE_ACCEPTS)
+ end
+ end
actionpack/lib/action_dispatch/railtie.rb で config.action_dispatch.strict_accept_header のデフォルトが false に設定され、Rails::Application のロード時にその値が ActionDispatch::Request に反映されます。
@@
- config.action_dispatch.ignore_accept_header = false
+ config.action_dispatch.ignore_accept_header = false
+ config.action_dispatch.strict_accept_header = false
@@
- self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
+ self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
+ self.strict_accept_header = app.config.action_dispatch.strict_accept_header
railties/lib/rails/application/configuration.rb の load_defaults に 8.2 用設定が追加され、strict_accept_header が true になるデフォルトが記述されています。
@@
- # existing defaults …
+ # existing defaults …
+ action_dispatch.strict_accept_header = true
ドキュメントと CHANGELOG も同様に追記され、config.action_dispatch.strict_accept_header の説明とデフォルト値が明示されています。また、テスト test_rfc9110_accept_header_respects_specificity が追加され、strict_accept_header: true が有効な場合に JSON や XML が正しく選択されることを検証しています。
設計判断
オプトイン方式が採用された理由は、既存アプリへの互換性を最優先したためです。フラグがデフォルトで false であることにより、現在稼働中のアプリは従来通り */* で HTML が返され、挙動の破壊を防げます。
config.action_dispatch.ignore_accept_header と strict_accept_header を同時に有効にした場合は、ignore_accept_header が優先される旨の警告がログに出力されます。これは設定衝突を明示的に知らせ、意図しない挙動を防止する設計です。
新規プロジェクトでは load_defaults 8.2 が自動で strict_accept_header を true に設定するため、モダンブラウザ向けの正しいコンテンツネゴシエーションがデフォルトで有効化されます。将来的に IE7 向けのワークアラウンドが完全に除去されることを見据えた段階的移行戦略と位置付けられます。
まとめ
strict_accept_header の導入は、古いブラウザ向けの例外処理をオプトイン化しながら RFC 9110 に準拠したコンテンツネゴシエーションを提供します。既存アプリは影響を受けず、新規アプリはデフォルトで正しい振る舞いが得られるため、Rails の HTTP 層が現代的な仕様と整合する重要な一歩となります。