Update a far-future expiring component

May 22nd, 2012. Tagged: performance

NOTE: This is late night mumbling from a week or two ago, leaving here for posterity. Don't read it. Meanwhile Steve wrote up a proper blog post which is highly recommended: Self-updating scripts. Read his post instead!

tl;dr: Load the component in an iframe, then reload()


Backstory

Steve Souders and I were chatting last week about his blog post and he was expressing his disappointment with the status quo of the short expiration time of those omnipresent third party scripts like the Facebook SDK (which loads Like button among others). We need short expiration on those because we need to be able to push quick fixes to critical bugs. And we cannot ask webmasters to keep up with versioned file names.

So the question is how do you set far-future Expires header of a static URL (like http://connect.facebook.net/en_US/all.js) and at the same time be able to update the content of this file without changing its filename.

Ahaa!

Today I had this sudden moment of clarity that we can use location.reload() of an iframe that contains the resource in question.

Sudden clarity

reload(true) forces uncoditional GETs.
reload(false) (default) sends coditional GETs.

So I can load the component in an iframe and then reload() the iframe. Cross-origin restrictions kick in when the static resource is on a CDN domain. This can be solved by loading an HTML page in an iframe and reloading it. All resources referred to by this page will be revalidated with a conditional GET.

Codeh

Far-future expiring component with a static URL:

<img src="http://stevesouders.com/images/book-84x110.jpg">

Version-checking iframe:

<iframe id="i" src="about:blank"></iframe>

Test-links:

<p><a href="javascript:loadit()">load image in iframe</a>
<p><a href="javascript:reloadit()">reload the image in the iframe</a>

And the JS code:

// load the version-checking html
function loadit() {
  document.getElementById('i').src = 'versioncheck.html';
}
 
// reload iframe
function reloadit() {
  window.frames[0].location.reload(false);
}

This is it. Here's the test page.

The versioncheck.html does nothing but include the static resource. Here it is.

Results

Safari, IE9, FF, Chrome are all consistent - when you reload() the versioncheck.html, conditional GETs go out to the HTML and all the resources in it. Then the server can reply 304 if the static resource hasn't changed. Or send a new file if the contents has changed.

Not entirely trusting the browser tools, also validated the 200 and 304 responses tailing the access log file on the server.

Conclusion

Plus: you can update the content of static URL with far-future Expires fairly easily.

Minus:
1. Additional 304s. Two if CDN domain is different than the page, 1 otherwise.
2. The users sees the updated resource on the next page view, sorta like App Cache

So for those omnipresent third party JavaScripts... They currently have short expiration times - between 20 mins and 2 hours. In that time if the user needs the script again, no requests go out.

But if you use the technique outlined, you can set their Expires header to 10 years in the future and for every single page view make two more conditional requests with likely a 304 Not Modified response. These extra requests, of course will not interfere with the user experience, because they will be lazy.

So you get faster user experience with more HTTP requests and delayed-till-next-page-view update.

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov