Protect IE from empty img src

In a previous post, I discussed the problem with setting an HTML image’s src attribute to an empty string. In Internet Explorer, Safari, and Chrome, this results in a second request being made to the server (Firefox 3.5 patched this behavior and Opera doesn’t exhibit the behavior). My post also showed a couple of ways to detect this issue on the server side at the time a request is received. I noted that it’s very difficult to detect these requests from Internet Explorer because it doesn’t send different Accept headers for image requests than it does for HTML requests. After a bit more investigation, I’ve found a way to prevent this behavior in Internet Explorer through version 8.

The tag

I’m still not entirely sure why this is true, but if you specify a base URL in the page using the <base> tag. For those unaware, the <base> tag is used to alter how URLs are resolved and linked to within a page. The href attribute is used to indicate the base URL from which relative URLs on the page should be resolved. This affects not just the <a> tag, but also any tags that accept a URL as an attribute value. Consider the following:

<img src="smile.gif">

To resolve smile.gif for this tag, the browser looks at the path of the containing page and then append smile.gif. So if the page is https://humanwhocodes.com/blog/, then the image is resolved to https://humanwhocodes.com/blog/smile.gif. This is how normal URL resolution works. Adding a <base> tag alters the default URL resolution. Example:

<html>
<head>
    <base href="/stories/">
</head>
<body>
    <img src="smile.gif">
</body>
</html>

If this page has a path of https://humanwhocodes.com/blog/, then the image’s URL is resolved to https://humanwhocodes.com/stories/smile.gif. That’s because the base URL is reset to https://humanwhocodes.com/stories/ by the <base> tag, so all URLs on the page are now resolved relative to that address. This is a convenient way to avoid duplicating the same URL information for every link on the page.

The approach

For some reason that I still don’t understand, the act of setting a base URL on a page causes Internet Explorer to ignore any <img src=""> that appears on the page, including <input type="image" src="">. As long as a tag appears on the page with an href specified, IE will no longer make a request to the server when one of these tags is present. You can try this yourself by viewing HTTP traffic while loading the following two pages (I recommend Fiddler):

If you have the possibility of an empty string image URL on your page, it would serve you well to set the base URL for the page. Since you probably don’t want to actually change how the URL is resolved, just set the base URL to the current path for the page. In PHP, you can use this code:

echo "<base href=\"{$_SERVER['REQUEST_URI'];}\">";

That way, you’re including the base URL to avoid the extra image request without changing how all URLs on the page are resolved.

Conclusion

Specifying a base URL on a page is a quick and easy solution to prevent an extra request due to an empty image source URL in Internet Explorer. It’s important to note that this has no effect on the other browsers with this behavior. For those, you’ll still have to use the server-side detection logic shared in the previous post to detect such a request and abort before server resources are used. At least for Internet Explorer, the solution is simple and can save your server.

Credits

Although I started running tests with this as part of an investigation based on a WHAT-WG mailing list discussion, Ben Alman hinted at this solution in a tweet to me that I apparently missed. Had I been paying attention, this would have been a much faster followup!

Comments

  1. "Cowboy" Ben Alman

    I love how my tweet makes me sound like a 14-year old girl, awesome!

  2. Kyle Simpson

    The <base> tag unfortunately creates some other crazy problems in IE6... I would highly recommend against using it... The bug is that the tag is treated as an unclosed tag (even if you explicitly close it), which means IE treats it as the parent tag for all the rest of the HTML content (including the entire <body>). This can wreak havoc with JavaScript that is adding and removing elements from the DOM. \

    jQuery for instance has a workaround in place which when doing JSON-P script tags, it insertBefore()s them to the head (in front of a base tag) instead of appendChild(). This merely ends around the problem by forcing the script tag to be part of the <head> in front of where the offending <base tag is. But it's not at all a solution for the fact that IE6 really behaves poorly with <base>. :(

  3. Nicholas C. Zakas

    @Kyle - that's interesting, though it sounds like the case you're worried about is if you have <base> in the <body>, is that correct? If so, does placing it in <head> fix this?

  4. Kyle Simpson

    Your message didn't come through the formatting very well, but i did view-source to see what it was you said. For posterity sake, the actual question is:

    @Kyle - that’s interesting, though it sounds like the case you’re worried about is if you have <base> in the <body>, is that correct? If so, does placing it in <head> fix this?

    Actually, the precise problem I was referring to is including the <base> tag in the <head> of the document... in IE6, whether you close it or not, the browser will interpret it as an unclosed tag, and "swallow" up everything after it in the markup.

    The problem with using it is that you may cause scripts which aren't aware of this bug to inadvertently fail if they are trying to add/remove nodes from the <head>, like code which adds <script> nodes... such as the JSON-P requesting in jQuery. Another example of code that would fail would be script loaders. LABjs, my script loader, for instance was vulnerable to this problem until the patch release I did this morning, in fact.

    As I said before, instead of doing "head.appendChild(obj)", the workaround currently is to do "head.insertBefore(obj,head.firstChild)". This just avoids the problem by making sure the scripts are added to the very beginning of the <head>, ahead of the <base> tag.

    But it's a bad fix, because it spec says that nothing should appear before the <base> tag, and also having the <script> nodes added in the reverse order can cause issues too. And it does nothing to address objects that are added/removed from other parts of the DOM and thus swallowed by the <base>. As I said, until a better fix is found, I would personally not recommend the use of <base> tag for pages that need to work correctly in IE6.

  5. Nicholas C. Zakas

    Fascinating, thanks for the heads up. I admit I tried IE7 and IE8 but neglected our old friend IE6. I'll have to play around with that some.

  6. Mark McDonnell

    Actually, the tag causes issues for me in IE7 (and that was placing it in the as well). It completely screws up trying to load scripts in parallel without blocking (ala Steve Souders instruction). This was a problem not only with a slight variation on the Steve Souders "Script DOM Element" method but also using LABjs! And it completely resolved itself once once I removed the tag??

    If anyone knows why this would happen then I would be very interested to hear about it :)

  7. Mark McDonnell

    corrections (seems the HTML tags were stripped out, my bad)...

    "...the *base tag causes issues..."

    and

    "...that was placing it in the *head as well..."

  8. surjit

    Can value of the SRC attribute of the HTML IMG tag = one Image file stored in another machine of a LAN.

    Secondly, can the path of the image reffered in the src attribute of the IMG tag = the path referrs to another directory other than the mapped to IIS where image is stored.

Understanding JavaScript Promises E-book Cover

Demystify JavaScript promises with the e-book that explains not just concepts, but also real-world uses of promises.

Download the Free E-book!

The community edition of Understanding JavaScript Promises is a free download that arrives in minutes.