Description

There is a flaw in the design of the conditional decorator, when using the Last-Modified functionality, that can cause the server to mistakenly report that a resource has not been modified, when in fact it has been.

Let's use the example from the documentation of a Blog having the last-modified time be based on the publication time of the latest Blog Entry.

Publication times of blog entries, when stored in the database have millisecond precision. But the Last-Modified HTTP header has only second level precision.

Here is the scenario:

The system clock of the server is currently at 10:00:01.100

A client requests the "front_page" view and gets a Last-Modified time of "10:00:01" (notice the milliseconds get truncated off)

Immediately after the request finishes, a new entry is published to the blog, and its publication time is set to the current time, which is now: "10:00:01.600" (notice it is still in the current second)

Some time later(possibly even much later), the client requests the "front_page" view, this time with an "If-Modified-Since" header with the value that it previously received of "10:00:01".

The server mistakenly returns a 304 Not Modified status code, because the new blog entry's publication time.

This may appear to be an extremely rare set of circumstances, but it can happen in practice if the client is doing very frequent polling. When it happens, the client will never find out about the new blog entry, even after the initial second is over, and possibly much longer into the future (until a new blog entry is published).

The solution: The client should ignore the "Last-Modified" header, and instead save "Date" header that is returned. (The server logic must now consider the resource modified in the case of If-Modified-Since == Last-Modified)

Due to this issue, the "Last-Modified" server header is useless and dangerous, and therefore the conditional decorator should not send it. It should instead send the "Date" header, which can actually be used correctly by the client.

If your application requires caching with sub-second precision, then you should use ETags (USE_ETAGS = True) and disable ​ConditionalGetMiddleware. ETags were added to the HTTP protocol precisely to cater for the use case you're describing. In Django they are handled by ​CommonMiddleware.