Data URIs, MHTML and IE7/Win7/Vista blues
2010 update:
Lo, the Web Performance Advent Calendar hath moved
Dec 7 This is the seventh in the series of performance articles as part of my 2009 performance advent calendar experiment. Stay tuned for the next articles.
UPDATE: While this post is an interesting study, the problem it solves turns out to be much simpler. The details are here. In resume: you need a closing separator and it all works fine in IE7/Vista/Win7
Let's start this post as a dialog:
- Data URIs are a way to embed the base64-encoded content of images inside HTML and CSS. Inlining images in markup or stylesheets helps you save precious HTTP requests. It's an alternative to CSS sprites.
- BUT! IE6 and IE7 don't support data URIs
- Yes, for them you can inline the images using MHTML
- BUT! Looks like IE7 on Vista and IE7 on Win7 have problems with MHTML
- [Sigh...] For those browser/OS combos there's a solution too.
I'll quickly go over what are data URIs and how MHTML addresses IE6 and IE7. If you're familiar with these, feel free to skip down to the Vista blues part.
Data URIs
Here's how to use data URIs in your pages:
- take an image:

- Use PHP to read this image's binary content and encode it using base64 encoding:
$ php -r "echo base64_encode(file_get_contents('horoscopes.png'));" iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAADAFBMVEX///8mPnru9... ...more scary stuff goes here... dajNGGzlDAAAAABJRU5ErkJggg== - To use this image in CSS, paste the encoded image content in the stylesheet following this syntax:
.horoscopes { background-image: url("data:image/png;base64,iVBOR...rkJggg=="); }Note: the trailing
==is part of the image content it's not part of the syntax - Alternatively if you want to use this image in the HTML, you can go like:
<img src="data:image/png;base64,iVBOR...rkJggg==" />
And that's about it for data URIs, it's quite simple actually.
Data URIs in the wild
To see real-life, high-traffic sites using data URIs look no further than your favorite search engines.
Yahoo! Search using data URI in CSS for a button background:

Google Search using data URI in an IMG tag for a video thumb:

Base64-encoded filesizes
The base64 encoding adds about 33% to the filesize. But, then you gzip the result, the compression brings the size back to the original. Plus or minus. Interestingly enough, sometimes after base64 and gzip, the result is smaller than the original. But don't count on that.
Theoretically also when you base64 encode a bunch of images and inline them in the same CSS or HTML, you'll have a larger string to compress, so increasing the chance of repetitions and compressing better. (Todo: test this assumption)
In any event, the benefit of reducing HTTP requests will likely greatly outweigh any fluctuation in the file size.
MHTML
IE8 supports data URIs, but earlier IEs do not. For them you can use MHTML (Multipart HTML, blogged previously here)
Continuing with the previous example, in order to display the horoscopes image in IE < 8 you can use a stylesheet like this:
/* Content-Type: multipart/related; boundary="_MY_BOUNDARY_SEPARATOR" --_MY_BOUNDARY_SEPARATOR Content-Location:horoscopes Content-Transfer-Encoding:base64 iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAADAFBMVEX///8mPnru9....U5ErkJggg== */ .horoscopes{*background-image:url(mhtml:http://example.org/styles.css!horoscopes);}
A few notes on this example:
- the image content is base64-encoded just like before
- if you need more images, use your chosen separator prepended with
--, in this case--_MY_BOUNDARY_SEPARATOR Content-Location:horoscopesis how you give a name to the image in order to use it later- then later in your stylesheet you can reference the name in the stylesheet using
http://url/styles.css!horoscopes - you need absolute URLs in the
mhtml:http://....part (that's retarded, hope I'm mistaken. Didn't work for me with relative URLs) - you can use a single CSS file to support both old IE as well as new browsers, but you'll have to repeat the image stream twice, probably a better approach is to use browser specific stylesheets
For more examples of MHTML (which is also used for multipart email messages) check this and for a brilliant example of using one MHTML to embed images in HTML (not CSS), check Hedger's test page here (appears 404 at the moment of writing). For a tool to automate the dataURI-zation/MHTML-ization, be sure to check Nicholas Zakas' CSSEmbed
The problem with IE7 on Vista (and Win7)
So far we have data URIs and MHTML fallback. Turns out we're not done yet, because IE7 on Vista and Windows 7 fails with the MHTML example. Two points:
- IE8 is the default in Windows 7, but it comes with several IE7 modes (either by default or turned on by users when their favorite pages break). Compatibility view, ie7 mode, ie8 in ie7 mode, blah-blah, it's all pretty confusing, but all modes with the exception of IE8 in proper IE8 standards don't work with the MHTML
- I tested Windows 7 evaluation copy, but I have the bad, bad feeling that Windows 7 normal copy will be as broken when it comes to MHTML. I believe it has something to do with security, if anyone finds an article on MSDN, or anywhere, please share.
The solution to the Vista problem is to split the CSS shown above (the one that uses MHTML) in two: one which contains the encoded images (the commented part) and a second one which only has the normal CSS selectors part.
In other words have something like:
mhtml.txt:Content-Type: multipart/related; boundary="_MY_BOUNDARY_SEPARATOR" --_MY_BOUNDARY_SEPARATOR Content-Location:horoscopes Content-Transfer-Encoding:base64 iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAADAFBMVEX///8mPnru9....U5ErkJggg==
No need to use comments. Could be .txt, .css or anything you like. In my tests the content-type didn't matter - text/html, text/css, text/plain all worked
styles.css:.horoscopes{*background-image:url(mhtml:http://example.org/mhtml.txt!horoscopes);}The normal CSS simply references the new MHTML file appending
!identifier- in your HTML you don't need to reference the MHTML document, you just point your
linktag tostyles.cssas usual andstyles.csscontains the reference to the MHTML doc
Time to celebrate? Almost.
Another ugly bug surfaces - this procedure outlined above works only once. Once the MHTML.txt is cached, it stops working. That means repeating visits to the page or other pages using the same mhtml. The effect also means hovering on the same page. Say you have two images - one normal and one mouse over, both in the same MHTML. The normal works and the hover breaks, mouseout breaks too. Insane, isn't it?
There's a solution though - you should make the browser request the MHTML document every time. This is kind of backwards when it comes to performance optimization, where we want to cache everything. It's pretty unfortunate. The best you can do is avoid sending the MHTML every time, but only send a "not-modified" header. In order for this to work, the browser has to send If-Modified-Since or If-None-Match.
So you can send your MHTML file with an ETag (yes, ETags can help sometimes
) Then the browser will send an If-None-Match and you can happily reply "304 Not Modified".
Sounds complicated? It sure is. And, remember, all this is only for IE7 (or any IE7 mode) on Vista and Windows 7. All other IEs on all other platforms work with the easy MHTML and IE8 works with data URIs.
Everything is a trade-off (see last night's post) and I can understand how you may think "That is one big tradeoff". There's always the option of serving normal images to the affected browser/os and just don't implement this optimization for them.
But it's good to know there is a solution.
DataSprites class
Let me offer this DataSprites PHP class I coded that takes care of all the scenarios. It takes a bunch of image filenames as input and produces:
- a CSS containing data URIs for normal browsers (example output)
- an MTHML CSS fallback for IE6,7 (example output)
- a CSS that refers to an additional MHTML for IE7 on Vista, Win7 (example output - css and mhtml). The MHTML in this case is sent out with an
ETag(derived from the input image filenames) and consecutive requests with the sameETagreturn304 Not Modified
You can see a test page here and view the source code here. The directory listing is also available if you want to grab the images for testing.
The perfect use case for such an approach is when you want to combine background images on the fly. When you would normally use CSS sprites, but you want to produce them dynamically at run time (maybe because you have too many combinations?).
I've tested this in Safari, Firefox, Opera, IE6, IE7 and IE8 (in all compat modes) on Vista and Windows 7 evaluation copy. If you find the test page is not working for you, please let me know.
In this DataSprites class, I'm checking the problem OS looking for the strings "Windows NT 6" and "Windows NT 7" in the user agent string. My Win7 evaluation copy had "Windows NT 6.1" in the UA, so I may be a little forwards-aggressive with the check, but somehow I thought Win7 proper should have "Windows NT 7" in the UA string. And also that Win7 will suffer from the same issue as Vista and Windows 7 evaluation.
UPDATE: Added a hover example.
UPDATE 2: Recorded a screencast to demo the IE7/Vista experience
Thanks!
Thanks for reading! Now you know all about data URIs, MHTML, and maybe a little too much about IE7/Vista's challenges
Ready to use data URIs and save some HTTP requests?
This entry was posted on Monday, December 7th, 2009 and is filed under CSS, IE, images, performance, php. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Get notification for future posts: follow me on Twitter or subscribe to my RSS feed

December 7th, 2009 at 5:27 pm
Sorry, but MHTML for IE7@Vista is still not works. I tested your page and also made hover test http://fullajax.ru/temp/datauri/phpied/test.html.
I think, there is no fully working solution on basis data:uri (mhtml) sprites for IE7@Vista. Unfortunaly, for IE7@Vista we need use normal standard images or standard css sprites.
December 7th, 2009 at 5:32 pm
Thanks Ruslan!
I don’t have Vista handy to test your example, but are you saying that my test page doesn’t work for you either?
December 8th, 2009 at 3:54 am
Yes, in your test page images displays only on first loading, after reload displays no images.
December 8th, 2009 at 11:33 am
I have IE7@Vista and can confrm Ruslan’s findings. The test page he links to works fine in Chrome. However the first time you view it with IE7 you will see the AOL icon in the Test Hover box. When you mouse over the AOL icon disappears. It does not come back when you mouse out of the element. On repeat views of the page no graphics at all appear in the Test Hover element.
December 8th, 2009 at 12:09 pm
hm, that’s exactly the result I was seeing before adding the ETag to cause the browser to make conditional requests. Maybe I broke something there
do you guys see the conditional get request going out (with 304 response) to the mhtml doc datasprites.php?extra=1
December 8th, 2009 at 2:31 pm
ok, got it, it’s the future Expires header I added that was causing the browser not to send a conditional get
fixed now – ie7/vista gets expires yesterday so they can send If-None-Match and the server returns 304
all others get future expires
added a hover test page:
http://phpied.com/files/datasprites/testhover.html
As you can see, not the best user experience, because there’s a quick flash while the conditional get returns 304 in IE7/Vista. So probably if the page has many hovers, best to stick with the normal sprites or individual images, otherwise data sprites are a viable (albeit complicated) solution.
December 8th, 2009 at 11:30 pm
and a video to illustrate ie8 vs ie7/vista – http://www.youtube.com/watch?v=xEXY15fV47Q
December 9th, 2009 at 8:50 am
@Stoyan, thanks fo video, very clearly.
I think, re-requests of mhtml css it is anti-optimization, browser always request large mhtml file.
December 9th, 2009 at 12:29 pm
Yep, Ruslan, it’s unfortunate. At least with the 304 response, we don’t have to send the mhtml every time, but it’s still a request, even if for a zero-size body.
December 25th, 2009 at 10:19 am
Windows 7 == Windows NT 6.1. It’s only a minor update to Vista.
February 1st, 2010 at 1:55 pm
We’ve been using this technique for a while and simply setting background images in the standard way for IE7 on Vista. However, we have run into a problem using mhtml with https. If the site is using https we get the “This webpage contains both secure and non-secure content” warning. Have you seen this before, and have you found a workaround?
February 2nd, 2010 at 10:08 am
fixed bug ie7/vista
/*
Content-Type: multipart/related; boundary="_MY_BOUNDARY_SEPARATOR"
--_MY_BOUNDARY_SEPARATOR
Content-Location:horoscopes
Content-Transfer-Encoding:base64
iVBORw0KGgoAAA....U5ErkJggg==
--_MY_BOUNDARY_SEPARATOR--
*/
via rfc2557 page7
April 14th, 2010 at 2:36 pm
aoao,
I did not understand your answer – Which bug did you solve and how?
For me, in rare cases, MHTML under IE8/Win7 in emulate-IE7 mode still doesn’t work well
July 12th, 2010 at 6:01 pm
Hi!
Finally I found a Temporary solution with IE7 on Vista, it was a headache , a nightmare.
Thank You!
P.S. @Prestaul very strange this should be appear with SSL certificate probles, I suggest to make a checkup!
August 17th, 2010 at 8:57 pm
Excellent post, can I translate it to Chinese and paste it to my blog?
November 16th, 2010 at 11:27 am
[...] by a comment at a previous post all we ever needed was to close the boundary delimiter and add two dashes at the end. The [...]
November 27th, 2010 at 8:33 am
Just found out the hard way something that should be pretty obvious.
If your site happens to have a meta http-equiv for X-UA-Compatible and the content attribute is set to IE=EmulateIE7 , this causes much dataURI pain for IE8. haha
After I set it to IE=8, all was good.
March 25th, 2011 at 2:02 pm
[...] Od dość dawna przeglądarki wspierają modyfikację taga “img” pozwalającego na wstawienie zamiast linka do pliku graficznego- jego postaci zakodowanej wg algorytmu base64 (dla przeglądarek IE 7 i starszych które mają z tym różne problemy są opisane metody “na około”, np. tu). [...]
March 28th, 2011 at 5:19 am
[...] 其实在测试页面我已经使用data URI内嵌图片给这个小图片保存整个HTTP请求。 [...]
July 29th, 2011 at 2:56 am
What tricks can I use to make images on my site appear to load faster?…
I see you have got a very good set of answers covering all areas, here’s one more. Data URIs can really help your site cut down on HTTP requests required to load a page. Read this post by Marcel on different approaches we took at Yahoo! to optimize im…
October 11th, 2011 at 8:32 am
free windows 7 themes…
[...]Data URIs, MHTML and IE7/Win7/Vista blues / Stoyan’s phpied.com[...]…
January 24th, 2012 at 8:21 am
filmy z napisami…
[...]Data URIs, MHTML and IE7/Win7/Vista blues / Stoyan’s phpied.com[...]…
February 10th, 2012 at 3:33 am
[...] 其实在测试页面我已经使用data URI内嵌图片给这个小图片保存整个HTTP请求。 [...]
November 2nd, 2012 at 7:33 am
i don’t understand the mhtml stuff, but i avoid ie.
however, both the sprite and base64 are loaded just once in the css file, right?
so there’s little difference
perhaps you could edit the base64 easier when you decided to delete one of the icons versus editing the sprite image file to remove the obsolete icon.
i suspect base64 method requires css, rather than inline in html, because duplicate base64 strings can’t be cached (i assume)
January 27th, 2013 at 4:05 pm
Heya admin, I just wanted to give you a quick heads
up that your Website url: http://www.phpied.
com/data-uris-mhtml-ie7-win7-vista-blues/ is being flagged as a potentially harmful web site in my browser chrome.
I would highly recommend having someone look into it. You
could certainly lose a lot of site visitors due to this problem.
Very best of Luck.
February 4th, 2013 at 1:07 am
[...] 其实在测试页面我已经使用data URI内嵌图片给这个小图片保存整个HTTP请求。 [...]