0

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
coool
  • 8,085
  • 12
  • 60
  • 80
  • Can you indicate the exact headers you are sending out? I don't think an error this big would have gone unnoticed. – scunliffe Jul 06 '14 at 01:32
  • Also be aware that if you are navigating your pages by clicking links the behavior should be correct **but** if you hit enter in the location bar the browser does send the conditional request to the server... so 304s are **expected** in this scenario! – scunliffe Jul 06 '14 at 14:49
  • 1
    @coool: Interesting question, but as already pointed out by scunliffe, hard to answer without the exact HTTP request _and_ response headers as captured by Fiddler. Also, I'd like to know what other browsers you tested; how's caching on IE11, Firefox, Chrome? And in case they behaved differently, did you notice any difference in the HTTP request (e.g. `If-Modified-Since`)? – Ruud Helderman Jul 06 '14 at 19:14
  • @scunliffe it is not ajax request..not from the browser location bar – coool Jul 07 '14 at 03:22
  • @Ruud I tried chrome/ff --> expired would not even sent a request to the server . and last-modified didn't make a difference – coool Jul 07 '14 at 03:25
  • @coool it looks like these are AJAX requests `X-Requested-With: XMLHttpRequest`... can you confirm that is what you are trying to test? – scunliffe Jul 07 '14 at 03:30
  • yes. It is a ajax request – coool Jul 07 '14 at 12:43
  • Does the problem only occur with content requested with XMLHttpRequest, or also if you load the same content directly in the web browser? Does the problem also occur with static content (e.g. an html file)? Please check your browser settings (Tools > Internet Options > Browsing history > Settings). If you change 'Check for newer versions of stored pages' to 'never', does the problem go away? Try the caching test proposed here: http://stackoverflow.com/questions/677480/which-browsers-have-problems-caching-xmlhttprequest-responses – Ruud Helderman Jul 11 '14 at 23:13

3 Answers3

1

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

Ruud Helderman
  • 10,563
  • 1
  • 26
  • 45
  • I am not sure what you mean. The cache-control is set on the response side. I tried setting up the cache-control both on req/resp as you suggested ..doesn't help..it sends a 304.. – coool Jul 07 '14 at 12:38
  • Apologies; apparently I totally misunderstood your question. Your original question states "otherwise it does a 200 for the resource which had an expiry in 1 year future". Sounds to me like you got 200 where 304 was expected, but now you seem surprised you _do_ get a 304. Please rephrase your original question; cut the prose ("doesn't work" makes no sense to me), just list the scenarios, with the relevant request/response headers, and your expectations where different from the actual behavior. – Ruud Helderman Jul 07 '14 at 15:43
  • yes I am surprised I get a 304 in IE..where as in chrome it doesn't even do a 304...it doesn't generate any request at all. And I am expecting the behaviour in IE as well..no 304..just serve from the cache when it has an expires..and about 200 --> it happens when there is no etag..please read my question again. The problem with etag is it goes through all the processing in the server side before saying it is the same etag..and zero bytes..but my server does work through different modules – coool Jul 07 '14 at 15:50
  • Thanks, your reply makes things clearer already, but there are still two pieces of the puzzle missing. Your latest test output only shows the scenario _with_ ETag, on IE (I assume). Please perform two more tests, and post Fiddler's output: (1) with ETag, on Chrome or some other modern browser, (2) without ETag and without If-None-Match (but _with_ `Cache-Control`), on legacy IE. – Ruud Helderman Jul 08 '14 at 10:53
0

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.

Community
  • 1
  • 1
Ruud Helderman
  • 10,563
  • 1
  • 26
  • 45
  • doesn't help. Thanks for your time though..let me know if you were able to replicate it. – coool Jul 09 '14 at 19:51
  • I finally managed to downgrade to IE9 and do some tests. I can confirm that `Expires: Sun Jul 05 2015 23:15:21 GMT-0400 (Eastern Daylight Time)` is invalid; I had to replace it with a valid RFC 1123 date to make caching work in IE9. Please see my edit above; try to match up with my response header. – Ruud Helderman Jul 12 '14 at 22:24
0

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.

Pierre
  • 429
  • 3
  • 15