F5 and XHR deep dive
In Ajax Caching: Two Important Facts from the HttpWatch blog, the author points out that:
…any Ajax derived content in IE is never updated before its expiration date – even if you use a forced refresh (Ctrl+F5). The only way to ensure you get an update is to manually remove the content from the cache.
I found this hard to believe, but it’s true. If you hit Reload (F5), IE will re-request all the unexpired resources in the page, except for XHRs. This can certainly cause confusion for developers during testing, but I wondered if there were other issues. What was the behavior in other major browsers? What if the expiration date was in the past, or there was no Expires header? Did adding Cache-Control max-age (which overrides Expires) have any effect?
So I created my own Ajax Caching test page.
My test page contains an image, an external script, and an XMLHttpRequest. The expiration time that is used depends on which link is selected.
- Expires in the Past adds an Expires response header with a date 30 days in the past, and a Cache-Control header with a max-age value of 0.
- no Expires does not return any Expires nor Cache-Control headers.
- Expires in the Future adds an Expires response header with a date 30 days in the future, and a Cache-Control header with a max-age value of 2592000 seconds.
The test is simple: click on a link (e.g., Expires in the Past), wait for it to load, and then hit F5. Table 1 shows the results of testing this page on major browsers. The result recorded in the table is whether the XHR was re-requested or read from cache, and if it was re-requested what was the HTTP status code.
|
Here’s my summary of what happens when F5 is hit:
- All browsers re-request the image and external script. (This makes sense.)
- All browsers re-request the XHR if the expiration date is in the past. (This makes sense – the browser knows the cached XHR is expired.)
- The only variant behavior has to do with the XHR when there is no Expires or a future Expires. IE 7&8 do not re-request the XHR when there is no Expires or a future Expires, even if control-F5 is hit. Opera 10 does not re-request the XHR when there is no Expires. (I couldn’t find an equivalent for control-F5 in Opera.)
- Both Opera 10 and Safari 4 re-request the favicon.ico in all situations. (This seems wasteful.)
- Safari 4 does not send an If-Modified-Since request header in all situations. As a result, the response is a 200 status code and includes the entire contents of the original response. This is true for the XHR as well as the image and external script. (This seems wasteful and deviates from the other browsers.)
Takeaways
Here are my recommendations on what web developers and browser vendors should takeaway from these results:
- Developers should either set a past or future expiration date on their XHRs, and avoid the ambiguity and variant behavior when no expiration is specified.
- If XHR responses should not be cached, developers should assign them an expiration date in the past.
- If XHR responses should be cached, developers should assign them an expiration date in the future. When testing in IE 7&8, developers have to remember to clear their cache when testing the behavior of Reload (F5).
- IE should re-request the XHR when F5 is hit.
- Opera and Safari should stop re-requesting favicon.ico when F5 is hit.
- Safari should send If-Modified-Since when F5 is hit.
edvakf | 11-Aug-09 at 6:47 pm | Permalink |
In Opera, F5 is like Ctrl-F5 of other browsers.
By the way, in the toolbar there is [Tools] > [Advanced] > [Reload from cache] menu. This is a reload solely from the cache (without sending any request at all).
That means if you view page source and modify it there, and do Reload from cache, your modification is reflected in the page.
Safari3.x didn’t use to send the If-Modified-Since header, but I found a page that says in Safari 4 Public Beta(5528.17), resources linked from the document you refresh gets the If-Modified-Since header.
http://d.hatena.ne.jp/ono_matope/20090606 (Japanese)
Maybe they changed the behavior at the last minute. (weird…)
Mats | 11-Aug-09 at 10:32 pm | Permalink |
What about Ctrl-R (in FF & IE). I think it specifically ignores any cached content and grabs new data.
Joao | 12-Aug-09 at 12:46 am | Permalink |
This XHR behavior is only with HTTP GET requests right?
POSTs will always be requested to the server.
Steve Souders | 12-Aug-09 at 8:13 am | Permalink |
@edvakf: I’m using Safari 4.0, the current version of Safari, but still don’t see the IMS header.
@Mats: Ctrl-R is the same as F5 – it uses what’s in the cache and sends the If-Modified-Since header. (Tested on IE7 and FF3.5.)
@Joao: I didn’t test POST because they’re bad for performance – they require an extra packet to complete the request.
Mats | 12-Aug-09 at 9:34 am | Permalink |
Sorry, I meant control-shift-R but I guess it’s the same as ctrl-f5 (only less work for your hand hehe :)
Might be worth a try.
Anup | 12-Aug-09 at 11:40 am | Permalink |
Steve, this is really useful. Thanks. Wondering what you find when the browser is closed. I seem to inconsistently find that some pages even with future expires are not cached when the browser is closed, and all content is re-requested from the server.
Anup | 12-Aug-09 at 11:42 am | Permalink |
Sorry — should have clarified a bit more — I find inconsistencies amongst different browsers and different sites (sites I can understand as some sites set future expiring dates etc, but on many browser I often see even future expiring items being rerequested when visited after the browser is re-opened).
The other consideration is closing browsers such that they do not re-open the same tabs that were just closed (as that always seems to come from cache).
Martin | 14-Aug-09 at 4:48 am | Permalink |
To solve this issue on IE we use ETags on XHR.
Good article Steave, thanks
Morgan Cheng | 14-Aug-09 at 3:39 pm | Permalink |
As a web developer, I found the F5 and Ctrl-F5 behavior is different in Firefox against IE. It’s not guaranteed to reload external javascript files from a page when hitting F5 in Firefox(both in 2.x and 3.x).
I once programmed something and tested it in Firefox. After hitting F5 or Ctrl-F5, the updated external javascript doesn’t get reloaded, even it is local.
Steve, can you give another article about behavior of F5 and Ctrl-F5 in different browsers?
David King | 22-Aug-09 at 8:33 am | Permalink |
Great article again Steve, although I’m more enamoured by the Yahoo best preactices page you’ve linked to… how have I missed that?
Dudy Adityawan | 06-Sep-09 at 8:26 am | Permalink |
My Friend this article really useful. Thanks. Wondering what you find when the browser is closed. I seem to inconsistently find that some pages even with future expires are not cached when the browser is closed.
vincent voyer | 11-Jan-10 at 7:54 am | Permalink |
I would say to never use ctrl+f5 when testing a web app because it triggers some very strange behavior on FF.
It will download all ressources every time you want to access it, without using cache ever, unless you do a simple f5 or better, open a new tab (or empty cache).
Empty your cache and hit ctrl+f5 gives two different behavior on FF.
Do not use ctrl+f5 :)
Ionatan Wiznia | 16-Feb-10 at 7:59 am | Permalink |
Alert developers!
This is also true for scripts if you load them via dom:
var element = document.createElement(“script”);
element.setAttribute(“type”, “text/javascript”);
element.src = ‘srcofthescript’;
document.body.appendChild(element);