flybywire
flybywire

Reputation: 273662

http: conditional get does not give a chance to refresh headers without sending body again

I don't know if this is a bug or a feature in the http spec, or I am not understanding things ok.

I have a resource that changes at most once a week, at the week's beginning. If it didn't change, then the previous week's resource continues to be valid for the whole week.

(For all our tests we have modified the one week period for five minutes, but I think our observations are still valid).

First we send the resource with the header Expires: next Monday. The whole week the browser retrieves from the cache. If on Monday we have a new resource then it is retrieved with its new headers and everything is ok.

The problem occurs when the resource is not renewed. In response to the conditional get our app (Java+Tomcat) sends new headers with Expires: next Monday but without the body. But our frontend server (apache) removes this header, because the spec says you should not send new headers if the resource did not change. So now forever (until the resource changes) the browser will send a conditional get when we would like it to continue serving straight from the cache.

Is there a spec compliant way to update the headers without updating the body? (or sending it again)

And subquestion: how to make apache pass along tomcat's headers?

Upvotes: 4

Views: 658

Answers (4)

RickNZ
RickNZ

Reputation: 18654

The Expires header was basically deprecated with HTTP 1.1; use Cache-Control: max-age instead.

Make sure you are including Last-Modified.

It's optional, but you may also want to specify Cache-Control: must-revalidate, to make sure intermediate proxies don't deliver potentially stale content.

You don't need to set ETag.

Example request:

GET http://localhost/images/logo.png HTTP/1.1
Accept: image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5
Referer: http://localhost/default.aspx
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: localhost
Connection: Keep-Alive

The response includes the requested content:

HTTP/1.1 200 OK
Cache-Control: max-age=10
Content-Type: image/png
Last-Modified: Sat, 21 Feb 2009 11:28:18 GMT
Accept-Ranges: bytes
Date: Sun, 18 Dec 2011 05:48:34 GMT
Content-Length: 2245

Requests made before the 10 second timeout are resolved from cache, with no HTTP request. After the timeout:

GET http://localhost/images/logo.png HTTP/1.1
Accept: image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5
Referer: http://localhost/default.aspx
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
If-Modified-Since: Sat, 21 Feb 2009 11:28:18 GMT
Host: localhost

The response is just headers, without content:

HTTP/1.1 304 Not Modified
Cache-Control: max-age=10
Last-Modified: Sat, 21 Feb 2009 11:28:18 GMT
Accept-Ranges: bytes
Date: Sun, 18 Dec 2011 05:49:04 GMT

Subsequent requests are again resolved from the browser's cache until the specified cache expiration time.

Upvotes: 0

Sripathi Krishnan
Sripathi Krishnan

Reputation: 31528

One way to solve the problem is using separate URIs for each week. The canonical url redirects to the appropriate url for the week, and instructs the browser to cache the redirect for a week. Also, URLs that have a date in them will instruct the browser to cache forever.

Canonical URL : /path/to/resource


Status Code : 301
Location : /path/to/resource/12-dec or /path/to/resource/19-dec
Expires : Next Monday

Week 1 : /path/to/resource/12-dec


Status code : 200
Expires : Never

Week 2 : /path/to/resource/19-dec


Status code : 200
Expires : Never

When the cache expires on Monday, you just send a redirect response. You either send last weeks URL or this weeks, but you never send the entire response body.

With this approach, you have eliminated conditional gets. You have also made your resources "unmodifiable-once-published", and you also get versioned resources.

The only caveat - redirects aren't cached by all browsers even though the http spec requires them to do so. Notably IE8 and below don't cache. For details, look at the column "cache browser redirects" in browserscope.

Upvotes: 0

johnstok
johnstok

Reputation: 98230

Try sending a valid HTTP-Date for the Expires header?

Upvotes: 0

edwardw
edwardw

Reputation: 13992

Just a Expires header is not enough. According to RFC 2616 section 13.3.4, a server needs to respond with two headers, Last-Modified and ETag, to do conditional GET right:

In other words, the preferred behavior for an HTTP/1.1 origin server is to send both a strong entity tag and a Last-Modified value.

And if the client is HTTP/1.1 compliant, it should send If-Modified-Since. Then the server is supposed to respond as following (quoted from Roy Fielding's proposal to add conditional GET):

  • If resource is inaccessible (for whatever reason), then the server should return a 4XX message just like it does now.
  • If resource no longer exists, the server should return a 404 Not Found response (i.e. same as now).
  • If resource is accessible but its last modification date is earlier (less than) or equal to the date passed, the server should return a 304 Not Modified message (with no body).
  • If resource is accessible and its last modification date is later than the date passed, the server should return a 200 OK message (i.e. same as now) with body.

So, I guess you don't need to configure Apache and/or Tomcat the way you described. You need to make your application HTTP/1.1 compliant.

Upvotes: 5

Related Questions