[34] [DFN[[CODE(HTTP)@en[[[If-Modified-Since:]]]]]] [[ヘッダー]]や
[DFN[[CODE(HTTP)@en[[[If-Unmodified-Since:]]]]]] [[ヘッダー]]は、
[[対象資源]]の最終更新日時が指定した時刻の前後いずれかによって[[要求メソッド]]の処理を実行するか否かを指定するものです。

* 仕様書

[REFS[
- [12] '''[CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-3.3>'''
- [25] '''[CITE@en[RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests]] ([TIME[2014-09-11 10:02:44 +09:00]] 版) <https://tools.ietf.org/html/rfc7232#section-3.4>'''
]REFS]

* 意味

[8] [CODE(HTTP)@en[[[If-Modified-Since:]]]] [[ヘッダー]]や
[CODE(HTTP)@en[[[If-Unmodified-Since:]]]] [[ヘッダー]]は、
[CODE(HTTP)@en[[[GET]]]] や [CODE(HTTP)@en[[[HEAD]]]]
の[[要求]]を[[選択された表現]]の最終変更日時に関する[[条件付き要求]]とします 
[SRC[>>12, >>25]]。 

[18] [CODE(HTTP)@en[[[If-Modified-Since:]]]] は、
[[キャッシュ]]されている[[実体タグ]]を持たない[[表現]]を効率的に更新するために使うことができます
[SRC[>>12]]。

[20] [[キャッシュ]]の更新に使う時は普通は[[キャッシュ]]された [CODE(HTTP)@en[[[Last-Modified:]]]]
の値を使って [CODE(HTTP)@en[[[If-Modified-Since:]]]] を[[生成]]します。
[[時計]]の同期が不十分な場合や何らかの理由で[[鯖]]側の [CODE(HTTP)@en[[[Last-Modified:]]]]
が過去に戻った時のために[[鯖]]が時刻を完全一致にしているような場合があるので、
これが最も[[相互運用性]]が高い動作です。しかし、[CODE(HTTP)@en[[[Last-Modified:]]]]
がない場合に [CODE(HTTP)@en[[[Date:]]]] や手元の時計の時刻を使うなどして、
それ以外の時刻を[[生成]]する場合もあります。 [SRC[>>12]]

[19] [CODE(HTTP)@en[[[If-Modified-Since:]]]] は、
[[Web]] の[[探索]]を最近の変更のみに限定するために使うことができます [SRC[>>12]]。

[21] [[利用者エージェント]]は取得の時間範囲を限定する場合には手元の[[時計]]の次オックを使ったり、
以前の[[応答]]の [CODE(HTTP)@en[[[Date:]]]] を使ったりします。
[[起源鯖]]が [CODE(HTTP)@en[[[Last-Modified:]]]] との完全一致を行っていると、
このような[[利用者エージェント]]でのデータ転送の削減の役には立ちません。 [SRC[>>12]]

;; [22] 完全一致するような[[起源鯖]]は実在するのでしょうか?

[27] [CODE(HTTP)@en[[[If-Unmodified-Since:]]]] は、
[CODE(HTTP)@en[[[POST]]]]、[CODE(HTTP)@en[[[PUT]]]]、
[CODE(HTTP)@en[[[DELETE]]]] のような状態を変更する[[要求メソッド]]において、
別の[[利用者エージェント]]が並列に加えた変更を上書きしてしまう「[[lost update]]」
問題を避けるために使われます。 [SRC[>>25]]

[28] [CODE(HTTP)@en[[[If-Unmodified-Since:]]]] は、
[[安全なメソッド]]において、[[選択された表現]]が既に一部または全部が蓄積されているものと一致しない時に要求を中断するために使うこともできます。
[SRC[>>25]]

;; [29] 例えば既に一部を[[キャッシュ]]している[[資源]]の残りの部分を取得したい時に、
途中で変更されていたらその処理を中断したいときに使えます。

* 構文

[13] [CODE(HTTP)@en[[[If-Modified-Since:]]]] [[ヘッダー]]と
[CODE(HTTP)@en[[[If-Unmodified-Since:]]]] [[ヘッダー]]の値は、
[[HTTPの日時形式]] ([CODE(ABNF)@en[[[HTTP-date]]]]) です [SRC[>>12, >>25]]。

[FIG(railroad)[
= [CODE(ABNF)@en[[[HTTP-date]]]]
]FIG]

* 処理モデル

[35] [[条件付き要求]]の項を参照してください。

[26] [[受信者]]は、[CODE(HTTP)@en[[[If-Modified-Since:]]]] や
[CODE(HTTP)@en[[[If-Unmodified-Since:]]]] 
の値を[[起源鯖]]の[[時計]]に照らして解釈しなければ[['''なりません''']]
[SRC[>>12, >>25]]。

[14] [[選択された表現]]の最終変更日時が [CODE(HTTP)@en[[[If-Modified-Since:]]]]
[[ヘッダー]]で指定された値と同じかそれ以前なら、
同[[ヘッダー]]の条件は[[真]]と判断しない[['''べきです''']]。 [SRC[>>12]]

;; [15] なぜ [['''MUST''']] でないのかは不明です。

[33] [[選択された表現]]の最終変更日時が [CODE(HTTP)@en[[[If-Unmodified-Since:]]]]
[[ヘッダー]]で指定された値よりも最近なら、同[[ヘッダー]]の条件は[[真]]と判断しては[['''なりません''']]
[SRC[>>25]]。

;; [16] [CODE(HTTP)@en[[[If-Unmodified-Since:]]]] は「同じかより前」、
[CODE(HTTP)@en[[[If-Modified-Since:]]]] は「より後」で、
等しい時刻の場合の動作が異なるので注意が必要です。

* 歴史

[305] [CITE[Request Headers in the HTTP protocol]]
( ([TIME[2001-11-29 11:01:38 +09:00]] 版))
<http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html#if-modified-since>

[REFS[
- [306] [CITE@en[RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1]] ([TIME[2012-01-09 20:55:20 +09:00]] 版) <http://tools.ietf.org/html/rfc2616#section-14.25>
]REFS]

[FIG(quote)[
[FIGCAPTION[
[11] RFC 2068・2616 (HTTP/1.1) 14.28 If-Unmodified-Since
]FIGCAPTION]

> The If-Unmodified-Since request-header field is used with a method to
make it conditional. If the requested resource has not been modified
since the time specified in this field, the server [DEL[should]] [INS[SHOULD]] perform the
requested operation as if the If-Unmodified-Since header were not present.

[CODE(HTTP)[If-Unmodified-Since]] 要求頭欄は、それを[[条件付]]とする[[方式]]と共に使います。
要求された[[資源]]がこの欄に指定された時刻から修正されていなければ、
[[サーバー]]は、 [CODE(HTTP)[If-Unmodified-Since]] 頭がなかった場合のように要求された操作を行う'''べきです'''。

> If the requested variant has been modified since the specified time,
the server MUST NOT perform the requested operation, and MUST return
a 412 (Precondition Failed).

要求された[[変種]]が指定されたい時刻より修正されているなら、
サーバーは要求された操作を行っては'''ならず'''、
[CODE(HTTP)[[[412]]]] (前条件失敗) を返さなければ'''なりません'''。

>
-If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date

> An example of the field is:

この欄の例:

>
-If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT

> If the request normally (i.e., without the If-Unmodified-Since
header) would result in anything other than a 2xx [INS[or 412]] status, the
If-Unmodified-Since header [DEL[should]] [INS[SHOULD]] be ignored.

要求が通常なら (つまり、 [CODE(HTTP)[If-Unmodified-Since]] 頭がなければ)
[CODE(HTTP)[[[2xx]]]] または [CODE(HTTP)[[[412]]]] 状態以外のものとなるならば、
[CODE(HTTP)[If-Unmodified-Since]] 頭は無視する'''べきです'''。

> If the specified date is invalid, the header is ignored.

指定された日付が不当なら、この頭は無視します。

[INS[
> The result of a request having both an If-Unmodified-Since header
field and either an If-None-Match or an If-Modified-Since header
fields is undefined by this specification.

[CODE(HTTP)[If-Unmodified-Since]] 頭欄ならびに [CODE(HTTP)[If-None-Match]]
頭欄または [CODE(HTTP)[If-Modified-Since]] 頭欄のいずれかの両者を有する要求の結果はこの仕様書では未定義とします。
]INS]
]FIG]

[FIG(quote)[
[FIGCAPTION[
[10] RFC 2326 12.23 If-Modified-Since
]FIGCAPTION]

> The If-Modified-Since request-header field is used with the DESCRIBE
and SETUP methods to make them conditional. If the requested variant
has not been modified since the time specified in this field, a
description will not be returned from the server (DESCRIBE) or a
stream will not be set up (SETUP). Instead, a 304 (not modified)
response will be returned without any message-body.

[1] [CODE(HTTP)@en[[[If-Modified-Since:]]]] [[要求頭欄]]は、
[CODE(HTTP)@en[[[DESCRIBE]]]] [[メソッド]]や [CODE(HTTP)@en[[[SETUP]]]]
[[メソッド]]で用いて、その[[要求]]を[[条件付]]とします。
要求された[RUBY[[[異体]]] [variant]]がこの欄で指定された時刻以後修正されていないなら、
[[鯖]]は[RUBYB[[[記述]]]@en[description]]を返さない ([CODE(HTTP)@en[[[DESCRIBE]]]]) か、
または[[ストリーム]]を設定しません ([CODE(HTTP)@en[[[SETUP]]]])。
その代わりに、[CODE(ABNF)@en[[[message-body]]]] 無しの [CODE(HTTP)[[[304]]]]
(無修正) [[応答]]を返します。

>
- [2]   If-Modified-Since = "If-Modified-Since" ":" HTTP-date

> An example of the field is:
- [3]     If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
]FIG]

** [CODE(HTTP)@en[length]] 引数

[6] 90年代後半の[[Webブラウザー]]は [DFN[[CODE(HTTP)@en[[[length]]]]]]
[[引数]]を [CODE(HTTP)@en[[[If-Modified-Since:]]]] [[ヘッダー]]に含めることがありました。

[REFS[
- [4] ''Nonstandard HTTP Headers'' <http://web.archive.org/web/20020610043404/http://www.dais.is.tohoku.ac.jp/~kabe/WWW/nonstdhdr.html#If_Modified_Since_length>
]REFS]

[5] >>4 [[WinIE3.0]]2 と [[NC]] 4.01 では確かに [CODE(HTTP)[[[length]]]] 引数がつきました。

[7]
[[Apache]] は[[時間帯]]以降 ([CODE(HTTP)@en[[[length]]]] も含めて)
無視するようになっているそうです。

[9]
>>5 [[WinIE 7]] と [[Firefox]] は (たぶん) つけないようです。

* 実装

[307] 
Last-Modified が If-Modified-Since と一致すると IE6 は他がなにあってもキャッシュを使っちゃう。
ETag とか Vary とかつけてみたけど無視される。

[413] [CITE@ja[ウェブマスター向けガイドライン - ウェブマスター ツール ヘルプ]]
( ([TIME[2014-10-28 08:32:00 +09:00]] 版))
<https://support.google.com/webmasters/answer/35769?hl=ja>

[414] [CITE@en[RFC 2660 - The Secure HyperText Transfer Protocol]]
( ([TIME[2014-11-09 14:12:37 +09:00]] 版))
<http://tools.ietf.org/html/rfc2660#section-5.2.5>

[FIG(quote)[
[FIGCAPTION[
[17] [CITE@en[Server timestamps — Kinto 8.1.5 documentation]]
([TIME[2018-02-21 01:03:20 +09:00]])
<https://kinto.readthedocs.io/en/latest/api/1.x/timestamps.html>
]FIGCAPTION]

> Changed in version 2.0: In previous versions, cache and concurrency control was handled using If-Modified-Since and If-Unmodified-Since. But since the HTTP date does not include milliseconds, they contained the milliseconds timestamp as integer. The current version using ETag is HTTP compliant (see original discussion.)

]FIG]
