coool
coool

Reputation: 8297

IE not respecting expires

IE (7 -10) doesn't seem to respect expires. I opened fiddler and was checking. if the response had a etag then it does a 304 otherwise it does a 200 for the resource which had an expiry in 1 year future. I tried setting last modified as well. it doesn't seem to work. In chrome when there is an expires tag..it doesn't even go out to the server(for a 304) it has it cached. Here is some of the Fiddler headers

Req Headers
GET /geoip/city?country=US&state=ID HTTP/1.1
X-Requested-With: XMLHttpRequest
Accept: */*
Referer: http://localhost/register/BG/57ac5960-f0d5-11e3-90d1-af2b2634c624
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Connection: Keep-Alive
Host: localhost
Cookie: connect.sid=s%3AntN3Tq9zXgrnlo5YOR1bsSa0lHE987Nv.aBbljhmG5tpfYcIXMgonxnhhWaWwd%2BTQ4jIKLnqL4us

Response Headers
HTTP/1.1 200 OK
X-Powered-By: Express
Vary: X-HTTP-Method-Override, Accept-Encoding
expires: Sun Jul 05 2015 23:15:21 GMT-0400 (Eastern Daylight Time)
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Date: Mon, 07 Jul 2014 03:15:21 GMT
Connection: keep-alive
Transfer-Encoding: chunked

and with E-tag

req Headers
GET /geoip/city?country=US&state=ID HTTP/1.1
X-Requested-With: XMLHttpRequest
Accept: */*
Referer: http://localhost/register/BG/57ac5960-f0d5-11e3-90d1-af2b2634c624
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: localhost
If-None-Match: W/"101c-2996882950"
Connection: Keep-Alive
Cookie: connect.sid=s%3AntN3Tq9zXgrnlo5YOR1bsSa0lHE987Nv.aBbljhmG5tpfYcIXMgonxnhhWaWwd%2BTQ4jIKLnqL4us

Response header
HTTP/1.1 304 Not Modified
X-Powered-By: Express
Vary: X-HTTP-Method-Override
expires: Sun Jul 05 2015 23:18:47 GMT-0400 (Eastern Daylight Time)
ETag: W/"101c-2996882950"
Date: Mon, 07 Jul 2014 03:18:48 GMT
Connection: keep-alive

As per suggestion from Ruud..Here is the req/response

GET /geoip/city?country=US&state=MO HTTP/1.1
Cache-Control: public, max-age=31536000
Accept: */*
X-Requested-With: XMLHttpRequest
Referer: http://localhost/register/BG/57ac5960-f0d5-11e3-90d1-af2b2634c624
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Connection: Keep-Alive
If-None-Match: W/"3bf9-3115988671"
Host: localhost
Cookie: connect.sid=s%3AZvwd9g7PAbQl7QHVx0ucpBMNnELll1R_.6KIvAtRWv9FK3zxxXVZfJBCpSv962zxLeTkvGd7mQq8

HTTP/1.1 304 Not Modified
X-Powered-By: Express
Vary: X-HTTP-Method-Override
expires: Mon Jul 06 2015 08:37:49 GMT-0400 (Eastern Daylight Time)
Cache-Control: public, max-age=31536000
last-modified: Sat Jul 07 2012 08:37:49 GMT-0400 (Eastern Daylight Time)
ETag: W/"3bf9-3115988671"
Date: Mon, 07 Jul 2014 12:37:49 GMT
Connection: keep-alive

Upvotes: 0

Views: 3104

Answers (3)

Ruud Helderman
Ruud Helderman

Reputation: 11028

One possible explanation is the fact that:

Expires: Sun Jul 05 2015 23:15:21 GMT-0400 (Eastern Daylight Time)

is not a valid RFC 1123 date. Try changing the response-header field to:

Expires: Sun, 05 Jul 2015 23:15:21 GMT

RFC 2616 states:

HTTP/1.1 clients and caches MUST treat other invalid date formats, especially including the value "0", as in the past (i.e., "already expired").

From there, the web browser's behavior depends on whether or not there is a Last-Modified and/or ETag response-header field.

  • Without Last-Modified / ETag: since Expires has an invalid date, and there is no Cache-Control to fall back on (you did test Cache-Control, but with Last-Modified / ETag, not without), caching may well be disabled; the client will send a request, the server will respond with a 200.
  • With Last-Modified / ETag: caching may be enabled, but Expires has an invalid date, so the client is forced to send a request to verify the cache is not stale. The server responds with a 304 to confirm the cache is OK.

From your story I gather this is precisely what IE 7-10 does.

On the other hand, RFC 2616 makes the note:

Recipients of date values are encouraged to be robust in accepting date values that may have been sent by non-HTTP applications, as is sometimes the case when retrieving or posting messages via proxies/gateways to SMTP or NNTP.

So some browsers may be more liberal than others, and do their utmost best to parse your date (Chromium issue 153759 seems to suggest just that). This might explain why Chrome hits the cache when IE does not.

Note: you may consider adding Cache-Control: public, max-age=31536000 to the response header, in addition to (or as a replacement of) Expires. See also:


EDIT: I did a very simple test with IE9. On a Linux machine, I repeatedly ran the following command (making it act as a single-shot web server):

cat h1.txt b1.txt | sudo nc -l 91

This is my h1.txt; since I created it on a Linux machine I had to use unix2dos to ensure each line was terminated with \r\n. Relevant response-header fields are Expires and Last-Modified.

HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Date: Sat, 12 Jul 2014 20:07:43 GMT
Expires: Sun, 05 Jul 2015 23:15:21 GMT
Last-Modified: Sat, 12 Jul 2013 20:07:43 GMT
Server: WEBrick/1.3.1 (Ruby/1.9.2/2014-01-23)
Content-Length: 252
Connection: keep-alive

b1.txt is an HTML page with a piece of JavaScript calling XMLHttpRequest, sending an HTTP request to the same page (because I was too lazy to set up another page).

<html>
<head>
<title>Test</title>
</head>
<body>
<button onclick="SendReq()">Send request</button>
<script>
function SendReq() {
    var rq = new XMLHttpRequest();
    rq.open('GET', 'http://192.168.1.103:91/', true);
    rq.send();
}
</script>
</body>
</html>

I didn't have Fiddler installed; I used Wireshark instead (filtering on tcp.port == 91) to monitor the traffic. And indeed, I can click the button on my web page as often as I want, there is no traffic, until I clear IE9's cache.

This means IE9's cache works alright; the problem must be on the web server; more specific, in the response header. Any small mistake could disrupt caching. Your original Expires header field (with the non-GMT date) was an excellent example of that.

And as pointed out by Pierre, do put Last-Modified and/or ETag in the response header, otherwise IE is likely to make the roundtrip to the web server even when the page is present in the cache. But I suspect that is the case for every web browser, not just legacy IE.

Upvotes: 0

Pierre
Pierre

Reputation: 430

I think you don't understant what the Expires is intended for. It's just something to tell to the browser: "keep it in cache until..." But it doesn't tell: "and don't check if it's not modified"! So use "Expires", and use "Last-modified" too, with both correct (valid RFC 1123) date value: "Sun, 05 Jul 2015 23:15:21 GMT". It should work.

Upvotes: 0

Ruud Helderman
Ruud Helderman

Reputation: 11028

Your HTTP request does not contain an If-Modified-Since header. This typically means the page has not been cached. This is probably due to the absence of a Cache-Control header. Without that header, a web browser will probably apply its own default caching behavior. In the case of IE7/8/9/10, that may well be something like: "Your URL has a query string, let's not cache it."

Please try adding this header to the HTTP request:

Cache-Control: public, max-age=31536000

Your HTTP request contains X-Requested-With: XMLHttpRequest, so I assume you are using XMLHttpRequest to send HTTP requests to the web server. You can probably add the header like this:

req.setRequestHeader("Cache-Control", "public, max-age=31536000");

In addition to the above, you may need to have a Last-Modified header in the HTTP response; the client may need this date/time to put in the If-Modified-Since header upon the next HTTP request.

Use Fiddler to verify that:

  • the HTTP request contains the header Cache-Control
  • the HTTP response contains the header Last-Modified
  • second time around, the HTTP request contains both Cache-Control and If-Modified-Since

When all else fails, you might even consider pushing your own If-Modified-Since header into the HTTP request.

Documentation of aforementioned headers can be found in sections 14.9, 14.25 and 14.29 of http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Upvotes: 1

Related Questions