Section 14.29 of the HTTP standard covers the Last-Modified header, which is mostly used as a weak validator. Here is an excerpt from the HTTP specification:

A validator that does not always change when the resource changes is a "weak 
validator."

One can think of a strong validator as one that changes whenever the bits of an 
entity changes, while a weak value changes whenever the meaning of an entity changes.

What this means is that we must decide for ourselves when a page has changed enough to warrant the Last-Modified header being updated. Suppose, for example that we have a page that contains text with a white background. If we change the background to light gray then clearly the page has changed, but if the text remains the same we would consider the semantics (meaning) of the page to be unchanged. On the other hand, if we changed the text, the semantics may well be changed. For some pages it is not quite so straightforward to decide whether the semantics have changed or not. This may be because each page comprises several components, or it might be because the page itself allows interaction that affects how it appears. In all cases, we must determine the moment in time when the semantics changed and use that moment for the Last-Modified header.

Consider for example a page that provides a text-to-GIF renderer that takes as input a font to use, background and foreground colors, and a string to render. The images embedded in the resultant page are generated on the fly, but the structure of the page is constant. Should the page be considered unchanged so long as the underlying script is unchanged, or should the page be considered to have changed with each new request?

Actually, a few more things are relevant: the semantics also change a little when we update one of the fonts that may be used or when we update the ImageMagick or equivalent image-generating program. All the factors that affect the output should be considered if we want to get it right.

In the case of a page comprised of several components, we must check when the semantics of each component last changed. Then we pick the most recent of these times. Of course, the determination of the moment of change for each component may be easy or it may be subtle.

mod_perl provides two convenient methods to deal with this header: update_mtime( ) and set_last_modified( ). These methods and several others are unavailable in the standard mod_perl environment but are silently imported when we use Apache::File. Refer to the Apache::File manpage for more information.

The update_mtime( ) function takes Unix's time(2) (in Perl the equivalent is also the time( ) function) as its argument and sets Apache's request structure finfo.st_mtime to this value. It does so only when the argument is greater than the previously stored finfo.st_mtime.

The set_last_modified( ) function sets the outgoing Last-Modified header to the string that corresponds to the stored finfo.st_mtime. When passing a Unix time(2) to set_last_modified( ), mod_perl calls update_mtime( ) with this argument first.

The following code is an example of setting the Last-Modified header by retrieving the last-modified time from a Revision Control System (RCS)-style of date tag.

use Apache::File;
use Date::Parse;
$Mtime ||= Date::Parse::str2time(
    substr q$Date: 2007/08/31 23:44:09 $, 6);
$r->set_last_modified($Mtime);

Normally we would use the Apache::Util::parsedate function, but since it doesn't parse the RCS format, we have used the Date::Parse module instead.