Accept ヘッダーの RFC 9110 準拠をオプトイン化する strict_accept_header 設定の追加

rails/rails

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.rbstrict_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.rbconfig.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.rbload_defaults に 8.2 用設定が追加され、strict_accept_headertrue になるデフォルトが記述されています。

@@
-            # 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_headerstrict_accept_header を同時に有効にした場合は、ignore_accept_header が優先される旨の警告がログに出力されます。これは設定衝突を明示的に知らせ、意図しない挙動を防止する設計です。

新規プロジェクトでは load_defaults 8.2 が自動で strict_accept_headertrue に設定するため、モダンブラウザ向けの正しいコンテンツネゴシエーションがデフォルトで有効化されます。将来的に IE7 向けのワークアラウンドが完全に除去されることを見据えた段階的移行戦略と位置付けられます。

まとめ

strict_accept_header の導入は、古いブラウザ向けの例外処理をオプトイン化しながら RFC 9110 に準拠したコンテンツネゴシエーションを提供します。既存アプリは影響を受けず、新規アプリはデフォルトで正しい振る舞いが得られるため、Rails の HTTP 層が現代的な仕様と整合する重要な一歩となります。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
c6691909

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
5回 (改善を経て承認)
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文、背景、技術的変更、設計判断(任意)、まとめが明確に配置されており、総論→各論→結論の流れが保たれています。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックのファイル名付きハイライトは正しい形式ですが、GitHubリンクが「[PR #57579](URL)」の形であり、要件の「[#123](URL)」形式になっていません。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

対象はRailsエンジニア向けで、専門用語が適切に使用されており、初心者向けの過剰な説明はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論・各論・結論のパラグラフで構成され、トピックセンテンスで始まり、1段落1トピック、段落長も適切です。空行で区切られています。

Diff内容との照合 ⚠ WARNING

コードブロックとDiff内容の一致

記事のコードブロックは `valid_accept_header` の変更を正しく示していますが、`mattr_accessor :strict_accept_header, default: false` の追加部分がコードブロックに含まれていません。内容は本文で言及されていますが、完全なDiff再現とは言えません。

技術用語の正確性 ⚠ WARNING

技術用語の正確な使用

PR説明では設定名が `config.action_dispatch.respect_accept_header_rfc9110` と記載されていますが、記事では `strict_accept_header` を使用しています。コードはDiffと合致していますが、PR情報との用語統一が取れていません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的な説明はDiffの変更点と一致しており、論理的に正しいです。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内の事実はPRの背景・目的・変更点と矛盾せず、根拠のない推測や数値的主張はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号、Issue番号、ファイルパスなどの数値・固有名詞は正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルはPRの内容を日本語で的確に表現しており、意味的に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

LTSやリリース日程等、PRに記載のない外部知識は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PRの記述と一致しています。