CSS Inline Images and the Data URI.

Embedding small image files directly into your CSS files makes sense. It saves HTTP requests so your page loads a lot faster. Delivering a load of small image files to a web browser is simply a waste of time. There is no doubt that web site development is moving away from graphic heavy sites and developers are starting to adopt standards, CSS being one of them. The other change is developers are now thinking about speed, and this is mainly in response to Google saying that fast pages do get an advantage in their search results.

As I waded through the huge amount of junk articles on the Internet it became apparent that there are no good guides on how to use such techniques. Embedded images and the Data URI make CSS sprites obsolete! So I didn’t want to hear about CSS Sprites. Most articles about embedded images talked about which browsers are supported but don’t offer a fail-safe technique that works on all web browsers. However it’s just not that hard. In fact it’s all quite easy. So here is our guide. I hope you enjoy it, and I hope it makes your website load faster.

The "Data:" URI

The first step to understanding how to embed images is understanding the "data:" URI. URI stands for Uniform Resource Identifier. It’s basically another word for a web address. You will notice at the start of each web address there is usually "http:". This first part basically indicates the protocol of the address. http: indicates that the HTTP protocol should be used to retrieve the file and that a web address will follow. There are others such as "http:", "ftp:", "mailto:" or "file:"

There is also a less used syntax that starts with “data:”. This syntax is not really all that new, however web browser support for it is. The data: URI syntax is quite unique because instead of an address the data of the file actually follows it. It is with this protocol that we can include images directly in HTML or CSS files. For the most part you will use it in CSS files rather than HTML and we will discuss the reasons for that shortly. Because web browser support is quite new, we also need an effective way to provide an alternative for older browsers. Fortunately this is easy and we will discuss it shortly. However, first lets have a look at the data: URI. An address will look something like this:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABlBMVEUzzDP////pTLhLAAAAHklEQVQImWP4fYDh5wGGHwcYGBwYoMABxAUK/j4AAKMyCtn7CO0NAAAAAElFTkSuQmCC

This creates this small green arrow: Green Arrow (This arrow is included with an inline Data URI, so if you can see it, then it works on your browser).

As you can see the address starts with “data:”. This indicates that the file’s data will actually follow as part of the URI. The second part is the MIME type. In this case we are telling the browser that the data following is PNG image. The encoding follows. base64 indicates that we are sending binary data rather than text and that it will be represented as base64. Base64 encoding is commonly used on the Internet to represent binary data in a text readable form. Following is the actual data of the file.

Notice how you can simply use this as the URL when you include an image in HTML. In fact you can even type the address into your browser window and it should display the picture.

<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABlBMVEUzzDP////pTLhLAAAAHklEQVQImWP4fYDh5wGGHwcYGBwYoMABxAUK/j4AAKMyCtn7CO0NAAAAAElFTkSuQmCC' alt='Green Arrow' width=10 height=10>

Or in CSS code as a background image.

body{
    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABlBMVEUzzDP////pTLhLAAAAHklEQVQImWP4fYDh5wGGHwcYGBwYoMABxAUK/j4AAKMyCtn7CO0NAAAAAElFTkSuQmCC');
}

Web Browser Support

Web browser support for the “data:” URI varies. The newest versions of all major web browsers support embedded images but older browsers don’t. Lets have a look at 7 common browsers.

Internet Explorer 7 : No
Internet Explorer 8 : Yes (Maximum size 32k)
Internet Explorer 9 : Yes
Opera : Yes
Firefox : Yes
Chrome : Yes
Safari : Yes

At the time of writing (2011) this level of support will cover about 90% of visitors. Note that while the “data:” URI is well supported for images it may not be supported for all file types. Internet explorer also has an important size restriction to consider. However as images can be important we would like to support all browsers. The simplest way to support all browsers is to use browser dependant CSS files.

Serving Browser Dependant CSS files with Apache

If you use the Apache web server, and most of the Internet does, you can simply serve a different CSS file for browsers that don’t support embedded images. This is also a method to take advantage of newer CSS properties that might not yet be supported by all browsers. The only disadvantage is that you need to maintain two different CSS files.

The first file is the version that will work on all browsers: (1.css)

background-image: url('/images/green-arrow.png');

The second version is the one that contains the embedded images. (1-inline.css)

background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABlBMVEUzzDP////pTLhLAAAAHklEQVQImWP4fYDh5wGGHwcYGBwYoMABxAUK/j4AAKMyCtn7CO0NAAAAAElFTkSuQmCC');

Both these files should reside in the same folder, but it’s best if this folder is separate from the rest of the site. I usually call it “/css/”. We then create a file called .htaccess which contains the instructions Apache uses to choose the file.

Header append Vary "User-Agent"
RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} ^Opera/[9]\.[0-9]+.*Version/1[0-9] [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[4-5]\.[0-9]+.*MSIE\W[8-9] [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[4-5]\.[0-9]+.*MSIE\W[10] [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[5-9]\.[0-9]+.*Chrome/(1[1-9]) [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[5-9]\.[0-9]+.*Safari/([5-9][1-9][1-9]) [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[5-9]\.[0-9]+.*Firefox/[4-9]
RewriteRule ^1\.css$ 1-inline.css [L]

You can amend the above Apache code to suit your site. I have designed it to support the feature set I am after. Basically all the current versions of today’s web browsers are supported. In the future you may need to occasionally add lines to support newer browsers, however if you don’t they will still get a fully functioning copy of the site but it may be a touch slower. Let’s have a look at each section so you can see how it works.

Header append Vary "User-Agent"

This line tells the browser to amend the “Vary” header for each file in the folder. This action is often overlooked by many programmers, however it is quite important. The vary header tells a proxy server that you are serving different content based on certain request headers. In this case it is the “User-Agent” header which contains the user’s browser version.

Many ISPs and corporate networks use proxy servers which cache your web page. This is great because it means users will get your pages much faster. However it’s important that a proxy server knows that the page returned will vary based on the User-Agent header otherwise some users could get unpredictable results. We only want this vary header to affect files in the /css/ folder. If the vary header affected all the pages on your site the proxy servers would become far less efficient. You should be using compression on your site so you will notice that the Vary header will also contain ‘Accept-Encoding’. This tells the proxy server that the server will return different information when different compression schemes are requested.

RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} ^Opera/[9]\.[0-9]+.*Version/1[0-9] [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[4-5]\.[0-9]+.*MSIE\W[8-9] [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[4-5]\.[0-9]+.*MSIE\W[10] [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[5-9]\.[0-9]+.*Chrome/(1[1-9]) [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[5-9]\.[0-9]+.*Safari/([5-9][1-9][1-9]) [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[5-9]\.[0-9]+.*Firefox/[4-9]
RewriteRule ^1\.css$ 1-inline.css [L]

The remainder of the code instructs Apache which css file to return to the user. I won’t give a full lesson on Apache here but you can see there are a number of rewrite rules. Each rule uses a regular expression to attempt to extract the browser string for each browser. If we find a supported browser the last line of code tells Apache to return "1-inline.css" instead of "1.css" which was requested.

You may also wish to add detection for bots that support the “data:” URI. The Googlebot might be a good example of this. But we will leave this decision to you. The important thing is that most of your users are now downloading your site with a lot less page requests. The remaining users or bots can still load your website without noticing any difference.

Advantages of Embedded Images

The main advantage of using embedded images in your CSS files is that your site will load faster. It could significantly reduce the number of requests that a browser needs to make to the server. This will reduce demand on your server. It will also mean that the user will have a copy of most of the website’s small images as soon as it downloads the CSS file. Typically this will be the second file that the browser loads. As a result the site will appear more responsive as the browser won’t load less important files like scripts and bigger images before the presentational images.

A CSS file is a great place to embed small images because it is normally only downloaded once for your site. If you have set the caching settings correctly the user and/or proxy server will keep a local copy of the file. Consequently subsequent page views will be a quicker. We recommend that CSS files have a long cache lifetime. If you make any significant changes to your CSS files then you can just give the new CSS file a different filename.

Using embedded images in a HTML file is a somewhat different story. Normally your HTML files have shorter cache lifetimes or may even be fully dynamic so they will not be cached at all. Another disadvantage of embedding images in HTML files is that they will not be cached for use on different pages. As a result a user will download more data overall. Embedded HTML images should be used sparingly but they can be useful for small images that are only used on one page.

Base64 encoding is less efficient than binary data. Typically Base64 data is one third larger than the binary equivalent. However there is no HTTP overhead when using data URIs so there is a saving. Generally images under 1k in size will have similar or better size efficiency than making a separate HTTP request for the image. The size disadvantage is somewhat overcome when you are using compression on your web server. Base64 data tends to compress well which reduces the transfer size. It will still be a touch larger the binary equivalent. There may also be compression advantages when multiple image of the same type are included in the same CSS file. Overall the reduced overhead and increased speed of using data URIs is more important than a slight increase in actual size.

Creating Data URIs

Creating data URIs from image files is actually quite easy. The only trick is encoding the file as base64. We have added an online tool that enables you to automatically generate data URIs just by uploading an image file. It supports files up to 100kb in size. Just bookmark the page and you can use it whenever you need to encode a file.

Base64 Encoder for Creating Data URIs

It is also possible to generate data URIs dynamically if that is what you need for your site. The PHP language has a simple base64_encode function that you can use to encode images. A quick sample of how to do this in PHP is shown here.

$imgfile = "arrow.png";
$imgbinary = fread(fopen($imgfile, "r"), filesize($imgfile));
$datauri = "data:image/png;base64," . base64_encode($imgbinary);

Summary

In summary embedded images are a powerful means of speeding up your pages. There is a particular advantage to embedding small CSS images. In some situations it may also be beneficial to embed images into HTML files, but cache performance should be considered. Difficulties with older web browsers not supporting embedded images is easily overcome by using browser specific CSS files.



7 Cynics

A cynical view of the world we live in.
Home
Web Design
CSS Design
Toolbox
Programming
OpenCL