[6] [DFN[[CODE(HTTP)@en[[[Last-Modified:]]]]]] [[ヘッダー]]は、
[[応答]]に含まれる[[表現]]の最終変更日時を表します。

* 仕様書

[REFS[
- [5] '''[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-2.2>'''
- [31] [CITE@en[RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)]] ([TIME[2014-09-21 17:04:59 +09:00]] 版) <http://tools.ietf.org/html/rfc4918#section-8.6>
- [33] [CITE@en[RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)]] ([TIME[2014-09-21 17:04:59 +09:00]] 版) <http://tools.ietf.org/html/rfc4918#section-8.8>
- [37] [CITE@en[RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)]] ([TIME[2014-09-21 17:04:59 +09:00]] 版) <http://tools.ietf.org/html/rfc4918#section-15>
-- [38] [CITE@en[RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)]] ([TIME[2014-09-21 17:04:59 +09:00]] 版) <http://tools.ietf.org/html/rfc4918#section-15.7>
]REFS]

* 意味

[9] [CODE(HTTP)@en[[[Last-Modified:]]]] [[ヘッダー]]は、
[[起源鯖]]が[[要求]]の処理の完了の時点において[[選択された表現]]が最後に変更されたと信ずる[[日時]]を表します
[SRC[>>5]]。

[47] 何をもって最終変更日時とするかは[[著者]]の判断に委ねられるところであり、
意味的には若干の曖昧性があります。[[ファイルシステム]]上の[[ファイル]]を[[応答]]として使う
([[静的ファイル]]を提供する) [[サーバー]]では、[[ファイルシステム]]上の[[更新日時]]を使うのが普通です。
しかし動的に生成される場合、複数の情報源 (データベース等) のデータを組み合わせて[[応答]]を作成することが多く、
何をもって最終変更日時とするべきかは定かではありません。その[[応答]]の作成の時刻
(つまり [CODE(HTTP)@en[[[Date:]]]] と同じ時刻) かもしれませんが、
それならそれを明示しても意味が無い場合もあるかもしれません。

[EG[
[48] 例えば[[ファイルシステム]]上の[[ファイル]]に[[広告]]を差し込んで[[応答]]とする場合は、
[[ファイルシステム]]上の[[ファイル]]の更新日時を使うべきで、差し込んだ日時を使うべきではないかもしれません。
しかし[[著者]]がその[[広告]]が当該ページの重要なコンテンツを形成するとみなすなら、
[[ファイル]]と[[広告]]の新しい方の日時を使うべきかもしれません。
]EG]

[49] 何が最終変更日時か[[著者]]にとっても明確でない時や、
[CODE(HTTP)@en[[[Date:]]]] と同じにするしかなく、それには意味が無いと[[著者]]が考えるときは、
省略するべきでしょう。本ヘッダーは必須ではありません。

[EG[
[50] 例えばデータベースから無作為に選択した内容を表示するときは、
本ヘッダーは省略した方が良いかもしれません。
]EG]

* 構文

[10] [CODE(HTTP)@en[[[Last-Modified:]]]] [[ヘッダー]]の値は、
[CODE(ABNF)@en[[[HTTP-date]]]] です [SRC[>>5]]。

[FIG(railroad)[
- [CODE(ABNF)@en[[[HTTP-date]]]]
]FIG]

* 文脈

[12] [[起源鯖]]は、[[選択された表現]]の最終修正日時が合理的かつ一貫的に決定できるなら、
[CODE(HTTP)@en[[[Last-Modified:]]]] を送信する[['''べきです''']] [SRC[>>5]]。
その値はできるだけ[[応答]]の [CODE(HTTP)@en[[[Date:]]]] [[ヘッダー]]を[[生成]]する時刻の近くの時点で得る[['''べきです''']]
[SRC[>>5]]。

[13] [[表現]]はいくつかの部分を組み合わせて構成されることがよくありますが、
その場合それらのうちで最も直近に変更されたものの日時が最終修正日時となるのが普通です。
[SRC[>>5]]

[14] [[時計]]を持つ[[起源鯖]]は、 [CODE(HTTP)@en[[[Date:]]]] より後の
[CODE(HTTP)@en[[[Last-Modified:]]]] を送信しては[['''なりません''']] [SRC[>>5]]。
[[メタデータ]]によると[[鯖]]の[[時計]]よりも後の最終修正日時となるような場合には、
[CODE(HTTP)@en[[[Date:]]]] の値で置き換えなければ[['''なりません''']] [SRC[>>5]]。

;; [15] これは[[キャッシュ]]の[[検証]]への悪影響を避けるためです [SRC[>>5]]。

[16] [[時計]]を持たない[[起源鯖]]は、他の信頼できる[[時計]]を持つシステムや[[利用者]]が指定している場合を除き、
[CODE(HTTP)@en[[[Last-Modified:]]]] を設定しては[['''なりません''']] [SRC[>>5]]。

;; [27] [CODE(HTTP)@en[[[ETag:]]]] の項も参照。

[28] 指定してはならない文脈については [CODE(HTTP)@en[[[If-Match:]]]]
の項を参照。

[29] [CODE(HTTP)[[[304]]]] の項も参照。

* タイムスタンプ

[32] [[WebDAV]] [[鯖]]は[[資源]]の[[本体]]や [[URL]] が変わっていないなら[[タイムスタンプ]]を変える[['''べきではありません''']] [SRC[>>31]]。

[34] [[WebDAV]] [[鯖]]は、 ([CODE(HTTP)@en[[[COPY]]]] や [CODE(HTTP)@en[[[MOVE]]]]
などの [[URL]] を操作する[[メソッド]]の結果であろうと、) [CODE(HTTP)@en[[[GET]]]] 
が返すであろうものが変わる度に[[タイムスタンプ]]を (その精度内で) 
変更しなければ[['''なりません''']]。 [SRC[>>33]]

;; [35] 場合によっては、移動・複製した範囲に含まれる複数の[[資源]]の[[タイムスタンプ]]を更新しないといけないかもしれません [SRC[>>33]]。

;; [36] 移動でタイムスタンプが更新されるのは必ずしも[[利用者]]の意図 (や多くの[[ファイルシステム]]の実装方法) ではない気がしますが...

* 処理

[17] [CODE(HTTP)@en[[[Last-Modified:]]]] [[ヘッダー]]の値が[[強い検証子]]か[[弱い検証子]]かは、
次のように決定できます [SRC[>>5]]。 
[FIG(steps)[
= [18] [[起源鯖]]が現在の実際の[[検証子]]と比較する場合は、
当該[[表現]]は元の[[検証子]]で示された[[秒]]に2度変更されていないと断言できるなら、
[[強い検証子]]です。
= [19] [[クライアント]]が当該[[表現]]の[[キャッシュ]]項目を有しており、
[[検証子]]を [CODE(HTTP)@en[[[If-Modified-Since:]]]]、
[CODE(HTTP)@en[[[If-Unmodified-Since:]]]]、[CODE(HTTP)@en[[[If-Range:]]]]
に指定しようとする場合で、その[[キャッシュ]]に [CODE(HTTP)@en[[[Date:]]]]
が含まれており、 [CODE(HTTP)@en[[[Last-Modified:]]]] が
[CODE(HTTP)@en[[[Date:]]]] より60秒[[以上]]前の場合には、[[強い検証子]]です。
= [20] [[中間キャッシュ]]が当該[[表現]]の[[キャッシュ]]項目を有しており、
その[[検証子]]と比較する場合で、その[[キャッシュ]]に [CODE(HTTP)@en[[[Date:]]]]
が含まれており、 [CODE(HTTP)@en[[[Last-Modified:]]]] が
[CODE(HTTP)@en[[[Date:]]]] より60秒[[以上]]前の場合には、[[強い検証子]]です。
= [21] それ以外の場合、[[弱い検証子]]です。
]FIG]

;; [22] この判定方法は、もし[[起源鯖]]が同じ[[秒]]に2回異なる[[応答]]を返していて、
そのどちらも同じ [CODE(HTTP)@en[[[Last-Modified:]]]] の値だったとすると、
少なくても一方の [CODE(HTTP)@en[[[Date:]]]] は [CODE(HTTP)@en[[[Last-Modified:]]]]
と一致するはず、という性質によっています。60秒としているのは [CODE(HTTP)@en[[[Date:]]]]
と [CODE(HTTP)@en[[[Last-Modified:]]]] が異なる[[時計]]から得られた場合などを想定しており、
短すぎると考えるならより長くとっても構いません。 [SRC[>>5]]

;; [26] [CODE(HTTP)@en[[[ETag:]]]] の項も参照。

[30] [CODE(HTTP)@en[[[Last-Modified:]]]] [[ヘッダー]]は[[キャッシュ]]における[[発見的満期時刻]]の決定にも用いられます。

;; [[発見的満期時刻]]を参照。

* [CODE(URI)@en[DAV:getlastmodified]] 特性 (WebDAV)

[39] [DFN[[CODE(URI)@en[[[DAV:getlastmodified]]]]]] [[特性]]は、
[[accept header]] なしで [CODE(HTTP)@en[[[GET]]]] した場合に返されるであろう
[CODE(HTTP)@en[[[Last-Modified:]]]] 
[[ヘッダー]]の値を表します [SRC[>>38]]。

[40] 値は [[HTTPの日時形式]]です [SRC[>>38]]。 [[OWS]] が[[ヘッダー]]に含まれる場合でも、
値には含める[['''べきではありません''']] [SRC[>>37]]。

[41] [[保護特性]]である[['''べきです''']] [SRC[>>38]]。
これは[[キャッシュ]]の適切な動作や [CODE(HTTP)@en[[[Last-Modified]]]] 
の値に依存する[[クライアント]]があるかもしれないからです [SRC[>>38]]。

[42] [CODE(HTTP)@en[[[COPY]]]] や [CODE(HTTP)@en[[[MOVE]]]] の後の値は、
終点資源の最終修正日時により決まります。 [SRC[>>38]]

[43] しかし実装によっては [[HTTP]] [CODE(HTTP)@en[[[Last-Modified:]]]]
の値が変わるべき時であっても、[[ファイルシステム]]の日時を使っているため、
変化しないこともあります [SRC[>>38]]。

[44] [CODE(HTTP)@en[[[Last-Modified]]]] は、[[本体]]の変更のみを反映する[['''べき''']]であり、
[[特性]]のみの変更では変更する[['''べきではありません''']] [SRC[>>38]]。
これは[[クライアント]]が既存の[[本体]]を上書きするべきかどうかの判断に
[CODE(HTTP)@en[[[Last-Modified]]]] を使うことがあるからです [SRC[>>38]]。

[45] [[WebDAV]] に従う[[資源]]が [CODE(HTTP)@en[[[GET]]]] への[[応答]]で
[CODE(HTTP)@en[[[Last-Modified:]]]] [[ヘッダー]]を返す場合は、
[CODE(HTTP)@en[[[DAV:getlastmodified]]]] [[特性]]を定義しなければ[['''なりません''']]
[SRC[>>38]]。

* 歴史

[2] [CODE(HTTP)[Last-Modified:]] 欄は HTTP では[[実体頭欄]]に分類されています。
ですから、[[要求]]でも[[応答]]でも、その[[メッセージ]]が
([CODE(HTTP)[[[HEAD]]]] のように一部省略されている場合も含めて)
[[実体]]を含んでいるのなら使えるように思えます。

しかし、 [[RFC 1945]] で [Q[sender]<urn:ietf:rfc:1945>]
とされていたところが、わざわざ [[RFC 2068]] 以後は
[Q[origin server]<urn:ietf:rfc:2068>] に直されていて、
要求で使えるような気配がまったくありません。

[3] [CODE(HTTP)[[[PUT]]]] や [CODE(HTTP)[[[POST]]]]
で実体を鯖に送る時には使えないのでしょうか。
([CODE(HTTP)[[[Content-Disposition]]]] でも使えというのでしょうか?)

[FIG(quote)[
[FIGCAPTION[
[7] RFC 1945 (HTTP/1.0) 10.10; RFC 2068・2616 (HTTP/1.1) 14.29 Last-Modified
]FIGCAPTION]

> The Last-Modified entity-header field indicates the date and time at
which the [DEL[sender]] [INS[origin server]] believes the [DEL[resource]] [INS[variant]] was last modified. [DEL[The exact semantics of this field are defined in terms of how the recipient should interpret it:  if the recipient has a copy of this resource which is older than the date given by the Last-Modified field, that copy should be considered stale.]]

[CODE(HTTP)[Last-Modified]] [[実体頭欄]]は、
その[DEL[[[資源]]]][INS[[[変種]]]]が最後に修正された日時と[DEL[[[送信者]]]][INS[[[起点鯖]]]]が信ずるところのものを示します。[DEL[この欄の正確な意味は、受信者がこれをどう解釈するべきかによって定義します。受信者がこの資源の複製を持っていて、それが [CODE(HTTP)[Last-Modified]] 欄に与えられた日付よりも古ければ、その複製は[[腐敗]]していると考えるべきです。]]

>
- Last-Modified  = "Last-Modified" ":" HTTP-date

> An example of its use is

使用例:

>
- [SAMP(HTTP)[Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT]]

> The exact meaning of this header field depends on the implementation
of the [DEL[sender]] [INS[origin server]] and the nature of the original resource. For
files, it may be just the file system last-modified time. For entities
with dynamically included parts, it may be the most recent of the set of
last-modify times for its component parts. For database gateways, it
may be the last-update time[INS[ ]]stamp of the record. For virtual objects,
it may be the last time the internal state changed.

この頭欄の正確な意味は、[DEL[送信者]][INS[起点鯖]]の実装と、
元の資源の性質に依存します。
元が[[ファイル]]であれば、[[ファイル・システム]]の最終修正時刻になるかもしれません。
動的に取り込まれる部分を含む実体では、その構成部分の最終修正時刻のうちで一番新しいものであるかもしれません。
[[データベース]]関門では、[[記録]]の最終更新時刻印かもしれません。
仮想物体では、内部状態が最後に変更された時刻かもしれません。

> An origin server [DEL[must not]] [INS[MUST NOT]] send a Last-Modified date which is later
than the server's time of message origination. In such cases, where
the resource's last modification would indicate some time in the
future, the server [DEL[must]] [INS[MUST]] replace that date with the message
origination date. [INS[An origin server [DEL[should]] [INS[SHOULD]] obtain the Last-Modified value of the entity as close as possible to the time that it generates the Date value of its response. This allows a recipient to make an accurate assessment of the entity's modification time, especially if the entity changes near the time that the response is generated.]]

起点鯖は、メッセージを生成する時点の時刻より後の日付を [CODE(HTTP)[Last-Modified]]
として送っては'''なりません'''。そうなる場合、
つまり資源の最終修正が将来になってしまうような場合は、
メッセージの生成の日付を [CODE(HTTP)[Last-Modified]] 
時刻としなければ'''なりません'''。[INS[起点鯖は、応答の [CODE(HTTP)[[[Date]]]] 値を生成する時刻とできる限り近い時刻に実体の [CODE(HTTP)[Last-Modified]] 値を得る'''べきです'''。そうすることで、たとえその実体が応答を生成した時刻の前後に変更されていたとしても、受信者が実体の修正時刻を正確に査定できます。]]

[INS[

訳注: [CODE(HTTP)[Last-Modified]] 値決定は [CODE(HTTP)[Date]]
決定時刻に近づけるよりも、対象となる実体そのものを生成 (取得)
する時刻に近づけるべきではないでしょうか。 (もちろん、
実体生成はできる限り [CODE(HTTP)[Date]] 決定に近づけるべきですが、
現実には必ずしもそうできる場合ばかりではないでしょう。)
]INS]

[INS[

> HTTP/1.1 servers SHOULD send Last-Modified whenever feasible.

HTTP/1.1 鯖は、 [CODE(HTTP)[Last-Modified]]
を可能であればいつでも送る'''べきです'''。
]INS]

[INS[

訳注: 注記なき修正部は RFC 1945→2068 の差分。
]INS]
]FIG]

[FIG(quote)[
[FIGCAPTION[
[8] RFC 2326 (RTSP/1.0) 12.24 Last-Modified
]FIGCAPTION]

> The Last-Modified entity-header field indicates the date and time at
which the origin server believes the presentation description or
media stream was last modified. See [H14.29]. For the methods
DESCRIBE or ANNOUNCE, the header field indicates the last
modification date and time of the description, for SETUP that of the
media stream.

[1] [CODE(RTSP)[Last-Modified]] [[実体頭欄]]は、
[[表現記述]]または[[媒体流]]が最後に修正されたと[[起点鯖]]の信ずる日時を示します。
[CODE(RTSP)[[[DESCRIBE]]]] [[メソッド]]や [CODE(RTSP)[[[ANNOUNCE]]]]
メソッドでは、記述の最終修正日時を表します。
[CODE(RTSP)[[[SETUP]]]] メソッドでは、媒体流の再修正日時を表します。
]FIG]

[4]
[CITE[HTML5 IRC logs: freenode / #whatwg / 20070626]] ([TIME[2007-06-26 22:14:23 +09:00]] 版) <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-151>
([[名無しさん]] [WEAK[2007-06-26 13:17:00 +00:00]])

* 実装

[23] [[ファイルシステム]]から[[静的]]にデータを供給する[[鯖]]では、
[[ファイルシステム]]の[[最終修正日時]]を [CODE(HTTP)@en[[[Last-Modified:]]]]
に設定するのが普通です。

[24] [[鯖]]で動的に生成した内容を返す場合には、生成する内容の種類や情報源により、
また実装に用いられている[[プログラミング言語]]や[[フレームワーク]]にもよりますが、
[CODE(HTTP)@en[[[Last-Modified:]]]] が指定されていることはそれほど多くはありません。
そのため[[Webブラウザー]]のキャッシュが有効に作用するであろう場面でも、
毎回[[鯖]]へのアクセスが発生することがよくあります。

[46] [[Apache]] は [[CGIスクリプト]]か指定した値を再整形して[[応答]]として使います。
そのため、[[CGIスクリプト]]の中には、[[曜日]]を適当に指定するなど、
[[Apache]] の再整形を期待して適当な出力をするものもあるようです。

* 例

[EG[
[11] 例 [SRC[>>5]]

[PRE(HTTP code)[
Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
]PRE]
]EG]

* 関連

[25] [[DOM]] では [CODE(DOMi)@en[[[Document]]]] [[オブジェクト]]に
[CODE(DOMa)@en[[[lastModified]]]] [[IDL属性]]があり、
[[HTTP]] [CODE(HTTP)@en[[[Last-Modified:]]]] [[ヘッダー]]の値にアクセスできます。

[FIG(quote)[
[FIGCAPTION[
[51] [CITE@en[Dockerfile reference]]
([TIME[2015-08-20 15:04:05 +09:00]] 版)
<https://docs.docker.com/reference/builder/>
]FIGCAPTION]

> If the remote file being retrieved has an HTTP Last-Modified header, the timestamp from that header will be used to set the mtime on the destination file. 

]FIG]


[FIG(quote)[
[FIGCAPTION[
[52] [CITE@ja[IPPSを使って文書を安全に印刷する]]
<http://support.brother.co.jp/j/s/support/html/hl3040cn_jp/ug/html/nug/7-15.html>
]FIGCAPTION]

> Last-Modified: Mon, 01 Jun 2009 08:28:54 GMT,Wed, 26 Feb 2014 03:02:54 GMT

]FIG]


[FIG(quote)[
[FIGCAPTION[
[53] [CITE@ja[はてなダイアリーの更新時刻 - World Wide Walker]]
([[yoosee]]著, [TIME[2016-03-22 06:36:11 +09:00]])
<http://yoosee.net/d/archives/2004/01/21/004.html>
]FIGCAPTION]

> 五月雨作者のakr さんの日記 によれば、五月雨は基本的に他のアンテナを信用せず、lirs 等の更新情報を元にもう一度自分でチェックに行くらしい。且つコンテンツを GET で fetch するため、HEAD にのみ last-modified フィールドを返すはてなダイアリー 等の場合、更新時刻を正しく取ることが出来ていないようで、ステータスが '''['''NoLM''']''' になる。はてなダイアリーの 2003-03-24 の記述 を見ると、HEAD だけではなく GET リクエストの場合にも last-modified フィールドを返すようにした、と言っている。だが実際に d.hatena.ne.jp にアクセスしてみてみると、

]FIG]


[FIG(quote)[
[FIGCAPTION[
[54] [CITE[Are Last Modified Headers Recommended? - Google プロダクト フォーラム]]
([TIME[2017-07-21 15:01:29 +09:00]])
<https://productforums.google.com/forum/#!category-topic/webmasters/crawling-indexing--ranking/6A_ctOfMoZw>
]FIGCAPTION]

> From my experience, Google doesn't keep track the Last-Modified header of the page but keeps track of the last time googleboot visited the page. So Google comes with a if-modified-since header that is not related to a page Last-Modified header. 

]FIG]
