01.27.09
Using javascript to change img src attribute, far-future Expires headers and cacheing
The Leitrim Design House web site went down the other day because it had exceeded its bandwidth allocation. This caught me by surprise - it’s a relatively small site for a niche audience and I was quite sure I had put it on a hosting plan with enough available resources. The server statistics revealed that the same 6 images were requested over 80,000 times each on a single day. Huh?
The pictures in question appear in a slideshow on the site’s home page. There is a javascript component that fades the images into each other every 10 seconds or so. Using Live HTTP Headers I could see that every time the image changed a new HTTP request was being sent. On my machine I was getting a status code 304 and the image wasn’t being requested every time but it seems this wasn’t the case everywhere (local cache disabled? Badly-behaved browser? It’s hard to tell.). So, what to do?
I decided to take a belt-and-braces approach on this to prevent a recurrence (and to avoid incurring the wrath of the nice people at blacknight). The first thing was to have a look at the javascript and stop it requesting images unnecessarily. The original slideshow code worked like this:
- clone the DOM node containing the first image in the slideshow
- put the new image under the old one (using z-index and absolute positioning)
- change the src attribute of the lower image to contain the URL of the next image in the slideshow
- wait a few seconds
- fade the upper image to 0 opacity
- swap the Z position of the 2 images
- set the lower image to full opacity
- go to step 3
I could see that a new HTTP request was going out at step 3 above, even when it was reloading an image that had previously been displayed. So I changed the code to this:
- clone the original image node N-1 times (for a slideshow with N images)
- hide all but 2 images
- put the visible images 1 on top of the other (using z-index and absolute position)
- wait a few seconds
- fade the upper image to 0 opacity
- hide the upper image and increase the z-index of the lower image
- unhide the next image
- go to step 3
The new code loads all the images at once and makes no more requests once they are loaded. There are some obvious problems with this code
- what happens if there are hundreds of images?
- there will be a stampede of HTTP requests which will inevitably lead to timeouts and missing images
- potentially unlimited amounts of memory will be used to store all the images
- what if the wait period between displaying images is shorter than the time it takes to load them all?
- once again there will be partially loaded/missing images
However, I’m a firm believer in the agile mantra “You’re not gonna need it“. For this application there are no more than 6 images in any slideshow, the images are all under 50kB and the wait time is about 10 seconds so the above problems shouldn’t arise. If I need to refactor in future, so be it.
Finally, I realised that I hadn’t configured apache to set the Expires header on images to a date far in the future. I added a couple of lines in the top-level .htaccess fileto make the images expire in 5 years. This means that on repeat visits to the site, if there is a copy of an image in your browser cache, the browser won’t even check to see if the image has been modified.