vBulletin 2.2.0
When retrieving a URI that contains URI avatar.php, the following HTTP response fields appear:
Cache-control: max-age=31536000
Expires: Tue, 12 Nov 2002 09:27:48GMT
Content-disposition: filename=tst.gif
Last-modified: Mon, 04 Jun 2001 17:34:46GMT
Content-type: image/gif
Content-length: XXXX
Notice the bold lines: the first two of them say that the file returned is valid for a year (31536000 is the number of seconds in a year). If the client and any proxy cache in the middle is HTTP/1.1 compliant and both Expires and Cache-Control fields exist (as in this case), then the Cache-Control field takes precedence over the Expires one and the specific file (the user's avatar in this case) is assumed to be "valid" for year, since that is what Cache-Control specifies. However, if either the client or any intervening proxy caches are HTTP/1.0 compliant, then the Cache-Control header is not understood. Only the Expires header is understood. The exact problem is that the date included in the Expires field is incorrect, since no spaces exists between the time (17:34:46) and the timezone! Behaviour of a client/proxy is unpredictable in this case and clearly is a bug. Additionally, and for the same reasons, the date listed in Last-Modified is not a valid RFC1123 date: no space between the time and the timezone.
Second problem: after downloading an avatar image, some seconds later I made the same request. The response that comes back can be the following:
Cache-control: max-age=31536000
Expires: Tue, 12 Nov 2002 09:27:48GMT
Content-disposition: filename=tst.gif
Last-modified: Mon, 04 Jun 2001 17:34:46GMT
Content-type: image/gif
Content-length: 1769
Now assume that the Expires/Last-Modified bug above is fixed and contains a valid date. Notice that Cache-Control specifies that the response will be considered "fresh" (i.e. a client/proxy will not re-request the image again) until an interval of 31536000 seconds has passed from the time the request was made at Mon, 12 Nov 2001 09:27:48 GMT. The expiration date is obviously Tue, 12 Nov 2002 09:27:48 GMT, as given by the Expires field. The actual "error" is the following: Cache-Control:max-age/Expires values should be associated with files that are not going to change sooner than the actual expiration date! An example: you might change your avatar image, but the users will not see the change! That's because the browser (or a proxy server in the middle that intercepts your browser's request) will see that the avatar that should be downloaded has not expired, therefore it is fresh and should be returned immediately to the user without anything being transferred over the net! That is a problem.
And the third one, which is a general matter of HTTP caching. Instead of specifying relative expiry dates (with Cache-Control and have Expires dates be calculated on the fly) it would be better to be the other way around. That is, absolute expiration dates should be used when possible. This can be done for both HTTP/1.1 and HTTP/1.0 clients proxies, by specifying an absolute date in an Expires field and by don't specifying Cache-Control at all. That is because, even changing a field in an element's HTTP response header, actually means that the element is modified, which might break some proxies and/or clients. My proposal would be to associate only Expires headers for those elements that can be more or less safely be the same for an interval (obviously not for user avatars), using the following algorithm:
When retrieving a URI that contains URI avatar.php, the following HTTP response fields appear:
Cache-control: max-age=31536000
Expires: Tue, 12 Nov 2002 09:27:48GMT
Content-disposition: filename=tst.gif
Last-modified: Mon, 04 Jun 2001 17:34:46GMT
Content-type: image/gif
Content-length: XXXX
Notice the bold lines: the first two of them say that the file returned is valid for a year (31536000 is the number of seconds in a year). If the client and any proxy cache in the middle is HTTP/1.1 compliant and both Expires and Cache-Control fields exist (as in this case), then the Cache-Control field takes precedence over the Expires one and the specific file (the user's avatar in this case) is assumed to be "valid" for year, since that is what Cache-Control specifies. However, if either the client or any intervening proxy caches are HTTP/1.0 compliant, then the Cache-Control header is not understood. Only the Expires header is understood. The exact problem is that the date included in the Expires field is incorrect, since no spaces exists between the time (17:34:46) and the timezone! Behaviour of a client/proxy is unpredictable in this case and clearly is a bug. Additionally, and for the same reasons, the date listed in Last-Modified is not a valid RFC1123 date: no space between the time and the timezone.
Second problem: after downloading an avatar image, some seconds later I made the same request. The response that comes back can be the following:
Cache-control: max-age=31536000
Expires: Tue, 12 Nov 2002 09:27:48GMT
Content-disposition: filename=tst.gif
Last-modified: Mon, 04 Jun 2001 17:34:46GMT
Content-type: image/gif
Content-length: 1769
Now assume that the Expires/Last-Modified bug above is fixed and contains a valid date. Notice that Cache-Control specifies that the response will be considered "fresh" (i.e. a client/proxy will not re-request the image again) until an interval of 31536000 seconds has passed from the time the request was made at Mon, 12 Nov 2001 09:27:48 GMT. The expiration date is obviously Tue, 12 Nov 2002 09:27:48 GMT, as given by the Expires field. The actual "error" is the following: Cache-Control:max-age/Expires values should be associated with files that are not going to change sooner than the actual expiration date! An example: you might change your avatar image, but the users will not see the change! That's because the browser (or a proxy server in the middle that intercepts your browser's request) will see that the avatar that should be downloaded has not expired, therefore it is fresh and should be returned immediately to the user without anything being transferred over the net! That is a problem.
And the third one, which is a general matter of HTTP caching. Instead of specifying relative expiry dates (with Cache-Control and have Expires dates be calculated on the fly) it would be better to be the other way around. That is, absolute expiration dates should be used when possible. This can be done for both HTTP/1.1 and HTTP/1.0 clients proxies, by specifying an absolute date in an Expires field and by don't specifying Cache-Control at all. That is because, even changing a field in an element's HTTP response header, actually means that the element is modified, which might break some proxies and/or clients. My proposal would be to associate only Expires headers for those elements that can be more or less safely be the same for an interval (obviously not for user avatars), using the following algorithm:
- Calculate the last second of the current year.
- Express the date in RFC1123. For year 2001, for example, the date would be Mon, 31 Dec 2001 23:59:59 GMT
- Form an Expires field by appending the date previously computed. So the complete field would be:
Code:Expires: Mon, 31 Dec 2001 23:59:59 GMT
- Send this field with the rest of the HTTP response for that field.
Comment