This is a cross post of Fantastic front-end performance Part 1 – Concatenate, Compress & Cache – A Node.JS Holiday Season, part 4, an article that I wrote for Mozilla Hacks. I reduced the content of the article to talk only about the three Cs, leaving the portion on connect-cachify for another article.
Three Cs of client side performance
The web is full of information related to performance best practices. While many advanced techniques exist to tweak every last millisecond from your site, three basic tools should form the foundation - concatenate, compress and cache.
The goal of concatenation is to minimize the number of requests made to the server. Server requests are costly. The amount of time needed to establish an HTTP connection is sometimes more expensive than the amount of time necessary to transfer the data itself. Every request adds to the overhead that it takes to view your site and can be especially problematic on mobile devices where there is significant connection latency. Have you ever browsed to a shopping site on your mobile phone while connected to the Edge network and grimaced as each image loaded one by one? That is connection latency rearing its head.
SPDY is a new protocol built on top of HTTP that aims to reduce page load time by combining resource requests into a single HTTP connection. Unfortunately, at the present time only recent versions of Firefox, Chrome and Opera support this new protocol.
By combining four requests into one, the total amount of time the browser is blocked due to latency will be significantly reduced.
Data URIs and image sprites are the two primary methods that exist to reduce the number of requested images.
A data URI is a special form of a URL used to embed images directly into HTML or CSS. Data URIs can be used in either the src attribute of an img tag or as the url value of a background-image in CSS. Because embedded images are base64 encoded, they require more bytes but one less HTTP request than the original external binary image. If the included image is small, the increased byte size is usually more than offset by the reduction in the number of HTTP requests. Neither IE6 nor IE7 support data URIs so know your target audience before using them.
Image sprites are a great alternative whenever a data URI cannot be used. An image sprite is a collection of images combined into a single larger image. Once the images are combined, CSS is used to show only the relevant portion of the sprite. Many tools exist to create a sprite out of a collection of images.
A drawback to sprites comes in the form of maintenance. The addition, removal or modification of an image within the sprite requires a congruent change to the CSS.
Sprite Cow helps you get the background-position, width and height of sprites within a spritesheet as a nice bit of copyable css.
Removing extra bytes - minification, optimization & compression
Combining resources to minimize the number of HTTP requests goes a long way to speeding up a site, but we can still do more. After combining resources, the number of bytes that are transferred to the user should be minimized. Minimizing bytes usually takes the form of minification, optimization and compression.
Two CSS minifiers include YUICompressor and UglifyCSS.
Images frequently contain data that can be removed without affecting its visual quality. Removing these extra bytes is not difficult, but does require specialized image handling tools. Our own Francois Marier has written two blog posts on working with PNGs and with GIFs.
Smush.it from Yahoo! is an online optimization tool. ImageOptim is an equivalent offline tool for OSX - simply drag and drop your images into the tool and it will reduce their size automatically. You don't need to do anything - ImageOptim simply replaces the original files with the much smaller ones.
If a loss of visual quality is acceptable, re-compressing an image at a higher compression level is an option.
The Server Can Help Too!
Even after combining and minifying resources, there is more. Almost all servers and browsers support HTTP compression. The two most popular compression schemes are deflate and gzip. Both of these make use of efficient compression algorithms to reduce the number of bytes before they ever leave the server.
Concatenation and compression help first time visitors to our sites. The third C, caching, helps visitors that return. A user who returns to our site should not have to re-download all of the resources again. HTTP provides two widely adopted mechanisms to make this happen, cache headers and ETags.
Cache headers come in two forms and are suitable for static resources that change infrequently, if ever. The two header options are
Cache-Control: max-age. The
Expires header specifies the date after which the resource must be re-requested.
max-age specifies how many seconds the resource is valid for. If a resource has a cache header, the browser will only re-request that resource once the cache expiration date has passed.
An ETag is essentially a resource version tag that is used to validate whether the local version of a resource is the same as the server's version. An ETag is suitable for dynamic content or content can change at any time. When a resource has an ETag, it says to the browser "Check the server to see if the version is the same, if it is, use the version you already have." Because an ETag requires interaction with the server, it is not as efficient as a fully cached resource.
The advantage to using time/date based cache-control headers instead of ETags is that resources are only re-requested once the cache has expired. This is also its biggest drawback. What happens if a resource changes? The cache has to somehow be busted.
Cache-busting is usually done by adding a version number to the resource URL. Any change to a resources URL causes a cache-miss which in turn causes the resource to be re-downloaded.
For example if http://example.com/logo.png has a cache header set to expire in one year but the logo changes, users who have already downloaded the logo will only see the update a year from now. This can be fixed by adding some sort of version identifier to the URL.
When the logo is updated, a new version is used meaning the logo will be re-requested.
There are a lot of easy wins when looking to speed up a site. By going back to basics and using the three Cs - concatenation, compression and caching - you will go a long way towards improving the load time of your site and the experience for your users.