Base64 encoding assets — the what, when and how
In the last five years, the focus in front end web development in general has shifted from a world of “making it work” to “making it fast.” One way of speeding up the front end and optimise load times is asset compressing using Base64 encoding. But as with all tips and tricks, there are both up- and downsides.
The need for speed
The reasons for this focus on speed range from the fact that things now have to work on — at best — spotty cellular connections in highly congested areas during rush hour, to the simple fact that the average computer is now so fast, that “waiting” has moved from being a necessary evil of the Information Age (“it’s still much faster than a human could have ever done it!”) to being the greatest of first world nuisances. No matter the actual reason, though, gone are the days of the dynamic web being magic in itself. Today, your job as a front end web developer is as much about making it fast as it is about making it work.
One of the ways in which we have made things faster, is to reduce the number of requests necessary to serve a web page. The reason for this is, that every request incurs at least a 1 or 2 packet penalty as the browser has to first ask for a new piece of data and then receive some information about that data before it can finally get to the data itself. When getting data from halfway across the globe, this can easily amount to tenths of seconds for every request.
This has led to “hacks” like concatenation and minification, wherein a host of stylesheets and scripts are combined into single files and sometimes compressed further by removing as much size as possible by removing everything that makes it easy for humans to process the data such as comments, white space etc. Combined with proper browser caching, most sites today load just one stylesheet and one script that your browser will keep cached locally so after those first two initial requests, we never have to wait for them again. Often times, styles and scripts for an entire site are combined into one file, even though only small percentages are used on every page, with the initial overhead of the tradeoff being negated by the fact that it’s a “one time event.”
However, while stylesheets and scripts by virtue of their nature lend themselves to this hack, requests for images are not nearly as easy to get rid of. The most common technique is called “spriting”, in which multiple images are combined into one and background positioning is then used to shift the background image of elements that need to have a particular image. However, even with tools, spriting is hard to do and maintain and can easily cause unforeseen problems across different browsers, so most people opt out of it, meaning that you’ll often see 10–20 small images being loaded for a page, just to show a few icons.

Google uses sprites extensively.
Enter data URIs
Since 1998, we’ve actually had an alternative to spriting; data URIs. Well, in theory, at least — but, I’ll get back to that. Data URIs are a special URI scheme starting with data:
much in the same way that you're used to seeing special URI schemes like mailto:
and javascript:
. Whereas most URI schemes point to data at another location using a specific protocol like for example HTTP (hence the http:
), data URIs actually contain the data itself encoded using either a standard ASCII or Base64 encoding. This means, that data URIs can be used instead of the URLs you would normally use -- and we've saved ourselves a request.
Data URIs adhere to the following format, where everything inside brackets is optional but very often necessary:
data:[<content type>][;base64],<data>
Too cryptic? Let’s imagine you use one of the most popular Facebook icons on your site in a 32 x 32 pixel PNG version. Normally, your stylesheet would have the following rule for the element containing the icon:
a.facebook
{
background: url('images/social_facebook_box_blue.png');
}
Using a base64 encoded data URI, this now becomes:
a.facebook
{
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABOtJREFUeNrkV81rXUUUPzP3vveSvEBS41P8bohSqC6iKbiooLhxpWSrO8E/oCtFLbhyIbQYUEFx4cY/oN246qJ140KFSAWtoJuAUoWatMnLzf2Y4zlnPu5M733ZduENk5k3M3fOb875nd/MVYgI9/LRcI+f3DeUUnD29c+XAHELATepaxnEO2gr8G3nMT8Wt/28njF6dundS1Sf++Hyu3t+HeUbL7zxBRu/OjfK19ceX4H5uYGDRgsZEMOGi0FojAl1w3Vj2uJ+11Eft+u6guJwSms127TaS99ffm8v8QDvnI2f3TgJw0FOHrFe8SDIFpXUYF3bxatQN9LHNZeS+6sGFLWV1lQyONzfWzdotmjRNxMOsNuffOJ+GA1zyLSCPNNSBrmGYZ5Rf0bAbOG+nPoyqjOak+VK3uGiuTjwqt0dtVE2lQ9GvNnNDgc45gvzA3lZu4V4QVnIrWQrcj8Z1RQXHjdU9nYLuHNQgvEh4BA1LkQNc8MIAA6jW2u5C4AGV5YXxIx3f1ssBxiBCkAA9qclXP/tJry48RCcOb0qbtaKwbO72RNa+j747FsYZExOAkJ9cepHHkBH1qRHgPF8Jp0QzxGRF/n1j3/g/bc2YO2xE7C4uAij0SiA7iyEPoNCVqQAEP08biiphf3QZgC70wO5vV/A+qkVMT6ZTGA4HMrOE7sC1rgUNeIBxHSjOoYpRjnKDq0YNT6l2phy/fetA3L7gzAej8V4nucCIC5ZlgVQ6D0gNmaEgA0qgwl77Tso5AppaKx3xgtDcTsb8s83127AR19eExJ7o8p5wG7QzAqB27WxTMXGsg1NLEAOhMt5TzjLbgv706+/gwfum6c5pAVVTfOoVFYfbBjSEMRpCDt/3ZZdLczllOs25zExjkGIGNQzT03EcBz7o7KC8bwWoNZjHEIG0EhtTNPvAUZWElJD5JoeanEh/3lNkPSjf00AgTbd7mI8j+1PC1HAsqrEU6apBUwIw6w0ZLScpyI2xqYT1hhemhaVFH6/OKoC2RIABOywMC5btEWtOaRHgYT9HACb64SX5iiJb5vCFsDBtILNl9dg9dElEZk+D1x4+xWrmk6Uft+5BR9/dTXEX+q+45gHWUpZAwyqoOWWMxjIeGp1As89/YgwnwunX/w8v37S5b5V059u3KTdV60QBS/0hKAmI5oV12WCkgRKj2I2GOd97y0n6t/+5U8KS+nibuxmZimhEMUJvkpUFJ0WGKvxPbGf9fz4845zu1dBMysNLbNlVMXHgxMjY+t3Ll6x4aL2J+dfhWdPP5wYpFsVXzoC0Zr6KHE/ziIhuB3GZxJiq4TGvUjyIGBY0Mqy7OyYjVdHdxLSJQQ8VoqN3ZlHKmHBNg3lRZfPTFS5HdDvNBx4l3GM2O+kuC8E6ESkleR254jdRZU763ufaL6cgNGF9tgQeNkVowaT3UNQMXusgrZa0Tn7/ZEe+I7RMWw6aZgcx1VVth6IXR8Zx6hf9QhRSLfIYELA4JHuh8kun2DGhjmQL4lh5AkvNP0RwIhHvQTc7QDgj4aymLpDIyZQSqSwM4jvDcdxoL0D2FrGL/V54Bx/NBQH/9JJVpAsV0nsQhaEuM76pox3a8Lc6Itqm22FL7L4MnHmtQ+XaOqWu7cvB8YiBOPuppqy2YPzY1E7cbv7NLt+5UL30+x/+3X8nwADAGdc48ttRnltAAAAAElFTkSuQmCC');
}
Notice that we use image/png
as the content type to tell the browser how to interpret the data. The actual data, which follows the last comma, is, as indicated, encoded using base64 encoding. There are a ton of different ways of actually getting the base64 encoded version of an image, but if you're on a UNIX system like Mac OS X or Linux, you'll often be able to use thebase64
command in your terminal:
$ base64 my_image.png
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgC[..]
However, if you use an asset processing pipeline like for example Sprockets or a Grunt based setup, it’s very likely that you can get rid of almost all the manual work and just have the base64 encoding be a part of your pipeline.
Browser support
As I mentioned earlier, we’ve actually had data URIs since 1998, but like any standard, it takes quite a lot of time before it propagates to all major browsers being used in people’s homes, so we’ve been somewhat hampered in being able to actually use it in the wild in the past. Basically, though, any browser from the Internet Explorer 8 era going forwards supports data URIs in some capacity. However, be aware that some browsers — especially on older mobile devices — tend to have odd size limitations and corner cases, that you might want to test for, if you’re stuck in a compatibility hell and want to go pure data URI on some images.
However, due to the power of CSS, we can work around most of these problems by offering up both the conventional URL and the data URI. Only browsers that understand the data URI will use it, and the rest will default to just getting the image from it’s URL. But, be aware of ordering and make sure that the data URI is actually the last option provided as browsers will pick the last understandable statement:
a.facebook
{
background: url('images/social_facebook_box_blue.png');
background: url('data:image/png;base64,iVBORw0KGgoAA[..]');
}
A price to be paid
As you might have guessed, there is of course a bit of overhead in using data URIs rather than sending binary data due to the use of encodings. For binary data, base64 is the only viable option as the ASCII encoding is woefully inefficient for anything but plain text. Base64 encodings basically use 64 humanly readable characters to express bytes. 64 characters gives us the possibility to store 6 bits of information (2⁶ = 64) while a byte conventionally contains 8 bits of information giving 256 possible values (2⁸ = 256). This means that a binary byte will take up 8/6 bytes when base64 encoded. In other words, the size of a base64 encoded piece of data is 8/6, or more readably, 4/3 of the original size — a 33 % overhead.
We can use this information to choose when to actually use a base64 encoded data URI rather than loading the image using a separate request. However, to do this, we must first understand how data actually gets transmitted when using HTTP over the Internet. Basically, HTTP uses the TCP/IP protocol, which sends data in packets of traditionally 1,500 bytes. Due to some overhead, we can usually only expect to be able to use 1,428 of those. So, for the sake of argument, let’s say the usable packet size is 1,400 bytes.
When we perform a request, we first send a packet with some information asking for the data we want — that is, unless there’s a metric shit ton of cookies in there, in which case multiple packets will be necessary. The server then responds with a response head followed by the actual data. The overhead then, is somewhere on the order of a packet and a response head, which for most sites will be on the order of 200–300 bytes for a simple image. Summing up the numbers then, saving a request really saves us somewhere in the area of 1,700 bytes. Knowing the overhead, we basically “break even” when 33 % of the image’s file size is 1,700 bytes, meaning a file size of around 5 KiB. If the image is any bigger than that, we’ll be sending more data over the wire by using data URIs rather than doing an extra request.
However, the pure amount of data is not all that matters. While you can often get away with the relatively minor overhead of combining all the styles and scripts for your site into two files, adding all the small images in there as well will often wind up being a significant amount of data that the user has to wait for, even if it’s just one time. If you don’t use all the icons everywhere, that’s going to be a waste of everyone’s time, bandwidth and mental health.
Add it to your toolbox
Data URIs are a pretty nifty tool for optimising your web site, and by now it should be a part of everyone’s front end toolbox. But, like with any other tool, don’t assume it’s the be-all and end-all of optimisation. As a rule of thumb then, stick to using data URIs for images that are no larger than 5 KiB and only use it for images that are used most of the time.
Base64 encoded icons on Iconfinder
You can find base64 encoded data URIs for all icons on Iconfinder. When you are on the icon details page, select the dropdown next to the download button for the file format you want to use. In the menu, select ‘Get data URI (Base64 encoded)’.

In the modal, select and copy the text. Paste it in HTML or CSS and you are ready to go.
