Zach’s ugly mug (his face)

Zach Leatherman

Dear IE6: Please Cache my Images.

18 Oct 2008 Read in about 10 minutes

A few months ago, I took on a new role at Union Pacific Railroad as the lead architect for an internal project: a Union Pacific UI Library used in our IT department on all new web-based projects. Based on jQuery, it is similar to a project like jQuery UI, but it has a few more components and they’re branded to our color scheme and programmed to our usability requirements.

The project has allowed me to expand my knowledge of browser quirks, improving my frontend-fu with every closed support ticket. What does that mean for you? More blogs posts. Learn from my mistakes (and from those I’ve inherited).

This week presented an interesting, albeit decreasingly relevant scenario, where a customer was seeing performance problems when loading a simple web page in Internet Explorer 6. The only hint to the root cause was a claim that he witnessed “400 items remaining” in his status bar. Naturally, this took awhile, but at least we had somewhere to start: Apache access logs.

Consider the following (jQuery) code to add three new images to the page using JavaScript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "">
    <script type="text/javascript" src="jquery-1.2.6.min.js"></script>
    <style type="text/css">
    img.bacon { width: 100px; height: 67px; background-image: url(bacon.png); }
        <script type="text/javascript">
        jQuery(function() {
          jQuery('body').append('<img src="spacer.png" class="bacon"/>');
          jQuery('body').append('<img src="spacer.png" class="bacon"/>');
          jQuery('body').append('<img src="spacer.png" class="bacon"/>');

Note how each image being added dynamically has a background-image as well.

In a normal web browser (read: Internet Explorer 7+, Firefox *), this would result in the following Apache access.log (with an empty browser cache) with only 4 HTTP requests (ignoring favicon.ico, which is irrelevant to this study): – - [18/Oct/2008:22:59:09 -0500] “GET /ie6cache/ HTTP/1.1″ 200 657 – - [18/Oct/2008:22:59:09 -0500] “GET /ie6cache/jquery-1.2.6.min.js HTTP/1.1″ 200 55774 – - [18/Oct/2008:22:59:09 -0500] “GET /ie6cache/spacer.png HTTP/1.1″ 200 207 – - [18/Oct/2008:22:59:09 -0500] “GET /ie6cache/bacon.png HTTP/1.1″ 200 5416

In Internet Explorer 6, however, this is the resulting Apache access.log (empty browser cache) with 8 HTTP requests: – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/ HTTP/1.1″ 200 657 – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/jquery-1.2.6.min.js HTTP/1.1″ 200 55774 – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/spacer.png HTTP/1.1″ 200 207 – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/spacer.png HTTP/1.1″ 200 207 – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/bacon.png HTTP/1.1″ 200 5416 – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/bacon.png HTTP/1.1″ 200 5416 – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/spacer.png HTTP/1.1″ 200 207 – - [18/Oct/2008:22:57:48 -0500] “GET /ie6cache/bacon.png HTTP/1.1″ 200 5416

When a web browser parses your markup and sees three image tags with identical src attributes, it makes only one HTTP request to the server for that image, and the rest of the images are loaded from the local web browser cache (no more HTTP requests are made). This is standard behavior for all modern web browsers. But in IE6, as you can see from the above log, made 3 requests to the server for both spacer.png and bacon.png. Why?

Turns out there are two independent issues at play here:

  1. The classic IE6 background-image flicker problem. This has been documented extensively, and the problem is that IE6 bypasses its local web browser cache for any image requests made for CSS background-images inserted on the page dynamically. The good news for this one is that the fix is a simple one.

    if(jQuery.browser.msie && parseInt(jQuery.browser.version, 10) == 6) {
      try {
        document.execCommand("BackgroundImageCache", false, true);
      } catch(err) {}

    Note the simple check for IE 6 execution only. This is optional, but keep in mind that this problem is not present in IE7+. During your testing, also keep in mind that Mister Pixel reports (and I can confirm) that once you run this command, this setting will persist in your browser, even if you empty your cache and remove the code from your scripts. You’ll need to close your browser window to reset the setting to default.

    See also Dean Edwards’ and Hedger Wang’s studies on this issue for more information. But this is really only half the problem. This will fix the duplicate bacon.png HTTP requests, but we’ll still see 3 spacer.png HTTP requests in our logs.

  2. The other issue is a separate but equal problem with IE6 caching. When inserting images with identical src attributes, IE6 will always go to the server (which means an extra HTTP request) for a new image. This is a documented feature in IE6, and the fix they have listed on their page isn’t ideal. The best solution here is to eliminate the need for spacer.png altogether. I can put these bacon background’s on a div tag just as easily, emphasizing pragmatism over semantics in this case (it’s a small sacrifice, you’ll live). If you have an &lt;img&gt; tag with a source you actually need, why not move it to the background image on a div tag? Unfortunately here, the takeaway is that your logic may need to be restructured to workaround this issue.
    See also Mihai Bazon’s study for more information, but do keep in mind that grouping this issue with the background flicker problem is an incorrect categorization. They are two separate issues and the fixes represented on that page don’t really fix the issue at all. Implementing server side caching as a “fix” is like using a bandaid for a tourniquet. You don’t want an HTTP request to go to the server at all, I don’t care if the response is a 200 OK or a 304 Not Modified. No HTTP request is always going to be faster than a server side cached response.

These issues will present themselves more frequently if you have the “Every visit to the page” caching option enabled in your Internet Tools in Internet Explorer. Good luck.


➡ Load Disqus to Leave a Comment ⬅

It started really well, but your solution was very much specific to your own scenario. In your case, simply restructuring your code was sufficient, however in a lot of cases it is not. I would like to see a more complete discussion on this. For me, the solution was to set absolute content expiration on the images, mixed with the preloading that Microsoft's article suggested. The catch was that if any image/css/js file on the page didn't have absolute content expiration on it - you're pretty much doomed.

Zach Leatherman

31 Oct 2009 at 05:08PM

Nay! Just don't use an img src, css backgrounds on a span or div are a perfectly good alternative to solve the problem.

I just started studying HTML and this looks like Chinese to me ATM. Very interesting though, thanks.