Data URIs are one of the best techniques in CSS, allowing developers to avoid referencing external images and instead embed them directly into a stylesheet. The main advantage of this approach is to save HTTP requests.
HTTP requests are a huge performance bottleneck, and the reason techniques such as CSS image sprites have been popular for some time. Basically, if you can avoid requesting an extra file, not only does it save your server the work of looking up the file, but it also saves your user the download time. In fact, HTTP request management is so important, that it is the top issue in the Yahoo Performance Rules.
Data URIs are an excellent way to reduce HTTP requests and speed up your pages, so let’s walk through how to use them in all major browsers.
When To Use Data URIs
When used instead of an image sprite, data URIs save a single HTTP request, and every little bit counts. However they are even more useful for images that are difficult to include in sprite sheets, for instance custom list bullets that need a varying amount of whitespace.
Although data URIs are an excellent way to reduce HTTP requests, it doesn’t make sense to use them in every situation. Since they embed the raw file data directly in the stylesheet, data URIs can lead to stylesheet bloat if they are used heavy-handedly.
Data URIs are great for any imagery that is repeated on all the pages of your site. However, for page-specific images it is usually better to reference an external image in the stylesheet. Since the file data is embedded directly in the stylesheet, data URIs will be downloaded by all your site’s visitors, regardless of whether they hit the page with that particular image. That said, you can feel free to embed page-specific data URIs on the individual page, just take care not to include them in a site-wide stylesheet.
How To Use Data URIs
Fortunately embedding data URIs is relatively simple. First you’ll need to generate a text string of the raw image data. For this I like to use the Base64 Online Generator.
Once you have the image data, simply place it directly in your stylesheet as an inline background image:
blah {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANS ...
UhEUgAAABgAAAAYCAMAAADXqc3KAAADU5ErkJggg==");
}
Here we’ve used image/png
to specify the content type, but make sure to change this to image/jpg
or image/gif
depending on the MIME type of the image you’re embedding. Additionally make sure to keep the data URI all on one line without line-breaks.
Supporting Data URIs in IE
Firefox, Chrome, Safari and Opera all support data URIs. However, as you may have guessed data URIs are not fully supported in IE, so special accommodations need to be made for everyone’s favorite browser.
Data URIs in IE8
IE8 mostly supports data URIs with a few minor caveats. The main problem is that IE8 data URIs cannot exceed 32kb, however this is not a huge issue, since embedded images rarely exceed this limit.
Additionally, data URIs can only be used for a handful of HTML elements in IE8: <object>
, <img>
, <input type="image">
& <link>
. But this only concerns markup, and when it comes to CSS, IE8 allows data URIs on any element. Finally, IE8 data URIs can only be used in CSS declarations that accept a url()
parameter, however since data URIs are rarely used differently, this is basically a non-issue.
Data URIs in IE6 and IE7
While IE6 and IE7 don’t technically support data URIs, we can achieve something similar using MHTML and a technique pioneered by Stoyan Stefanov.
First include the images as MIME data within a stylesheet:
/*
Content-Type: multipart/related; boundary="MYSEPARATOR"
--MYSEPARATOR
Content-Location: image1
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAD....U5ErkJggg==
--MYSEPARATOR
Content-Location: image2
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAA....U5ErkJggg==
--MYSEPARATOR--
*/
Be careful with the separators here, or you will have issues with Vista / Windows 7. The boundary
declaration can be used to define any separator string you want, however be sure to start each new data block with --MYSEPARATOR
and end the MIME data with --MYSEPARATOR--
.
Next, reference the MHTML images in your stylesheet:
/*
The MIME data from above goes here
*/
.image1 {
background-image: url("data:image/png;base64,[raw data here]");
*background-image: url(mhtml:http://mysite.com/styles.css!image1);
}
.image2 {
background-image: url("data:image/png;base64,[raw data here]");
*background-image: url(mhtml:http://mysite.com/styles.css!image2);
}
Here we’re first including the standard data URI for most browsers, then using the star hack to define the MHTML image data for IE6 and 7. Note the url in the mhtml
declaration; it uses the stylesheet’s url followed by the Content-Location
defined in the MIME data section.
However this technique has one clear drawback, which is that we are now including the image data twice on one page. Considering the large size of raw image data, it doesn’t make sense to include it twice unless you’re embedding very small images.
Fortunately this issue can be avoided in a number of ways. One approach might be to use server side browser sniffing to only enable the MIME data for the affected browsers.
Alternately, you can use browser conditionals to include a separate stylesheet for IE7 and lower. This has several advantages, including being able to attach other browser-specific styling without relying on CSS selector hacks.
However, don’t include an IE7 stylesheet on top of your main stylesheet, or you’ll defeat the purpose of reducing HTTP requests. Instead include separate stylesheets for both:
<!--[if !(IE)|(gt IE 7)]><!-->
<link rel="stylesheet" type="text/css" href="main-styles.css" />
<!--<![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" href="ie7-styles.css" />
<![endif]-->
Here we’ve included the stylesheet main-styles.css
for IE8 and non-IE browsers, as well as ie7-styles.css
for IE7 and below. Although somewhat more difficult to maintain, this approach ensures the lowest number of HTTP requests (and these stylesheets can be built dynamically as part of a build process).
Using Data URIs For Fonts
Data URIs aren’t only useful for images, they’re also a great way to reduce HTTP requests for fonts embedded with @font-face.
Embedding fonts with data URIs is the same as embedding images, except with a different MIME type:
@font-face {
font-family: "My Font";
src: url("data:font/opentype;base64,[base-encoded font here]");
}
To generate the raw font data, you can use the base64 generator we discussed earlier, or better yet use Font Squirrel’s @font-face generator.
Simply use the “expert” mode and enable the “Base64 encode” option:
Unfortunately there are a few notable drawbacks to using @font-face data URIs. First, Font Squirrel states that SVG and EOT file types do no support data URIs. However as Aaron Peters proves, EOT can be supported (although SVG is still not an option).
Additionally, unlike images which use the same data URI across all browsers, @font-face uses several different browser-specific implementations. Considering the relatively large size of font files, it would be a mistake to embed all these font files in a single stylesheet. So similar to the MHTML example above, use server side browser sniffing, or a similar method to serve the data to only the appropriate browsers.
Thanks to Stoyan Stefanov for all his wonderful posts on Data URIs.
I’ve also found that Data URI also fails on my iPod Touch flavour of Safari.
Brilliant article – thank you! I will be using these when necessary in the future. 🙂
Thanks for the solution.
There’s a minor bug in your conditional comments that prevents non-ie browsers from seeing the first stylesheet. The fix is to use:
<!--[if !(IE)|(gt IE 7)]><!-->
<link rel="stylesheet" type="text/css" href="main-styles.css" />
<!--<![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" href="ie7-styles.css" />
<![endif]-->
Nice catch Peter, that’s fixed in the post now.
Screw the user. No developer is gonna do THAT! Sure if IE7+ supported it like the rest, but this is just way too much work for nothing extra. Who cares about 600ms extra load time!?
Also the conditional comments around the stylesheet sortof defeats the purpose… You might want to use ‘conditional divs’: http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
Scott,
What is your user agent?
Are you connecting via Verizon? I’m experiencing a handful of 404 errors, all of which are coming from users who connect through Alltel/Verizon. I found an article from last year which claims that Verizon parses js/css files to try to speed up loading of external resources: http://dandreifort.com/2009/10/20/verizon-alltel-facebook-errors/
Not sure whether they will accept responsibility, much less fix the issue, but this appears to be their fault.
Thanks for this useful information……:)
For ie6 + 7, regarding the url in the mhtml declaration, am I correct in assuming you have to define the protocol, hostname and css filename? If so, that makes life /really/ difficult while developing and then deploying. Just saying..
Didn’t know about this technique so thanks for sharing. However, regarding IE7 and IE6, it seems like way too much hassle adding the MIME data into the stylesheet. Because you are using conditionals I personally would just reference the external background image as normal for these browsers and take the hit. Hopefully as usage of these older browsers decreases with time, the extra bandwidth used by these would become less of an issue.
Hey, Jon, nice article! 😀
I was thinking about your solution of providing with a hack support for base64 images in IE7 and less, with the mhtml data images As you concluded it’s an inviable way, because we would doubling the raw data of the image.
Did that browsers need to recieve the best, even being the worst, with limited capabilities? There could be a better way, don’t you think?
Do you already read about the safe css hacks, right?. I lean to agree with his approach.
We could add the base64 data image as a background-image and provide to IE7- a hack background–image with the relative path to the image:
blah {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANS ...
UhEUgAAABgAAAAYCAMAAADXqc3KAAADU5ErkJggg==");
*background-image: url(../img/flyer-dog.png);
}
What do you think?
using css data URLs is very important for content security.
pepole won’t be able to steal images, design, etc.
very nice article, sums it up real good.
Good information. Thanks for sharing it.
Nice post and the best solution for css background 🙂 Thanks man!
this content is very important and useful because using css information URLs is very important for material protection.
pepole will not be able to grab pictures, style, etc.