If-Modified-Since ヘッダーが全ての HTTP‑date 形式を受け入れるようにパーサを修正

rails/rails

If-Modified-Since ヘッダーの解析ロジックが RFC 9110 で定義された三つの HTTP-date 形式すべてに対応するよう変更され、条件付き GET が期待通りに 304 Not Modified を返すようになりました。

背景

If-Modified-Since はクライアントが最後に取得したリソースの更新時刻をサーバに通知し、キャッシュの有効性を判定する重要なリクエストヘッダーです。

RFC 9110 §5.6.7 ではこのヘッダーが IMF‑fixdate, RFC 850, asctime の三形式を受け入れることが MUST と規定されていますが、Rails の従来実装は Time.rfc2822 を使用しており IMF‑fixdate のみを解析できました。その結果、RFC 850 または asctime 形式が送られた場合はパースに失敗し、not_modified?false になるためキャッシュが無効化され、フルレスポンスが再送されていました。

この不一致は Last-ModifiedDate ヘッダーがすでに Time.httpdate で統一的にパースされている点と対照的で、仕様遵守とキャッシュ効率の両面で修正が必要と判断されました。

技術的な変更

ActionDispatch::Http::Cache::Request#if_modified_sinceTime.rfc2822 から Time.httpdate へ置き換えられ、コメントで RFC 9110 の要件が明示されています。

@@
   def if_modified_since
     if since = get_header(HTTP_IF_MODIFIED_SINCE)
-      Time.rfc2822(since) rescue nil
+      # `If-Modified-Since` carries an HTTP-date, which per RFC 9110 may be in
+      # any of the three legal formats (IMF-fixdate, RFC 850, or asctime).
+      # `Time.httpdate` accepts all three; it also matches how the response side
+      # parses `Last-Modified`/`Date`. `Time.rfc2822` only accepted IMF-fixdate.
+      Time.httpdate(since) rescue nil
     end
   end

さらに actionpack/test/dispatch/request_test.rbRequestIfModifiedSince テストクラスが追加され、IMF‑fixdate、RFC 850、asctime の三形式が正しく解析され not_modified? が真になることを検証しています。

Time.httpdate は IMF‑fixdate、RFC 850、asctime の三形式すべてをパースでき、GMT タイムゾーンを必須とするため RFC 9110 に完全準拠します。既存の IMF‑fixdate での挙動は変更せず、後方互換性が保たれたまま新規形式がサポートされました。

設計判断

この修正は 最小限の侵襲で仕様遵守を実現 する方針に沿い、独自パーサを実装せず標準ライブラリの Time.httpdate を再利用しています。これにより実装コストとメンテナンス負荷が大幅に削減されました。

リクエスト側とレスポンス側で同一メソッド (Time.httpdate) を使用することで、コードベース全体の一貫性が向上し、将来的なバグ修正や拡張時のリスクが低減します。唯一のトレードオフは、Time.httpdate が GMT 以外のオフセット表記を拒否する点ですが、これは HTTP‑date の仕様に合致するため、非準拠クライアントは 304 を取得できないだけに留まります。

まとめ

If-Modified-Since が RFC 9110 が要求する三つの HTTP-date 形式すべてを受け入れるようになり、条件付き GET の正しいキャッシュ判定が保証されます。実装は Time.httpdate への置換というシンプルな変更であり、既存動作への影響はなく、コードベースの一貫性と仕様遵守が同時に達成されました。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文がタイトル直下にあり、背景・技術的変更・設計判断・まとめの各章が存在し、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ✓ PASS

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

コードブロックは ````ruby:actionpack/lib/action_dispatch/http/cache.rb```` 形式で正しく記述され、PRリンクは [#57505](URL) の形で正しくリンク化されています。

対象読者への適合性 ✓ PASS

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

Rails エンジニア向けの専門的な内容で、初心者向けの過剰な説明はなく適切です。

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

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

各セクションは総論・各論・結論の段落構成となり、段落はトピックセンテンスで始まり 1 段落 1 トピック、長さも適切です。

Diff内容との照合 ✓ PASS

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

記事中のコードブロックは提供された Diff と完全に一致し、追加されたテストクラスの記述も Diff 内容と合致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

HTTP-date、IMF‑fixdate、RFC 850、asctime、Time.httpdate などの用語が正確に使用されています。

説明の技術的正確性 ✓ PASS

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

技術的主張は PR の説明・Diff と整合しており、誤りや根拠のない記述はありません。

事実の突合 ✓ PASS

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

全ての主張が PR の Title、Description、Diff で裏付けられており、ハルシネーションは見られません。

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

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

PR 番号 #57505 が正しく記載されており、他の数値や固有名詞の誤りはありません。

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

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

記事タイトルは PR の意図「Parse all HTTP-date formats in If-Modified-Since」を正確に日本語化しています。

外部知識の正確性 ✓ PASS

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

記事は PR に記載された情報以外の外部知識(バージョンサポート状況等)を含んでいません。

時間表現の正確性 ✓ PASS

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

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