When I unset the Last-Modified header in Apache (ETags are also disabled), Firefox (4.01) will not cache any file regardless of whether I set a future Expires header or enable the Cache-Control header.
So is the Last-Modified (and/or an ETag) header required for browser caching?
From here:
If no validator (an ETag or Last-Modified header) is present on a response, and it doesn’t have any explicit freshness information, it will be considered uncacheable.
... well, if by "Freshness Information" they mean "Cache-Control" or "Expires" header, Firefox should be caching without the Last-Modified header.
EDIT FOR FURTHER FIREFOX INFO
Note that no attempt to generate a 304 on any PHP file served by Apache 2.2 in Firefox 4.01 is successful (reload, fresh visit, etc.) without a Last-Modified header, regardless of any combination being set of a valid caching Cache-Control header, the Expires header or both headers.
foo.php: content of this file simply echoes 'Hello World'.
HTTP/1.1 200 OK Date: Mon, 06 Jun 2011 14:04:58 GMT Server: Apache Cache-Control: public, max-age=3600 Expires: Fri, 01 Jul 2011 21:23:55 GMT Vary: Accept-Encoding Content-Encoding: gzip Content-Length: 1594 Keep-Alive: timeout=10, max=500 Connection: Keep-Alive Content-Type: text/html; charset=utf-8 EDIT FOR EVEN MORE STRANGE FIREFOX 4.01 FINDINGS
Even stranger, based on what I've seen with Firefox 4.01, no form of server-side cache control headers (Expires and/or Cache-Control) influence Firefox's caching behavior. Firefox only cares about Freshness information (Etag or Last-Modified).
In summary, if the file has been modified, Firefox reloads it, regardless of any Expires or Cache-Control headers. If the file doesn't contain any Freshness information, Firefox reloads it no matter what.
If anyone finds out differently in their observations, please update me.
ANOTHER EDIT
From this link:
13.2.1 Server-Specified Expiration
An expiration time cannot be used to force a user agent to refresh its display or reload a resource; its semantics apply only to caching mechanisms, and such mechanisms need only check a resource's expiration status when a new request for that resource is initiated. See section 13.13 for an explanation of the difference between caches and history mechanisms.
