Page loading time is crucial to keeping visitors on your site and
maximizing conversions. Studies have been done that show the maximum
time people are willing to wait for a page to load is less
than 5 seconds. Make them wait more than that, and it’s game over.
They’ll hit their back button, never to return. It’s vitally important,
then, to make sure your web site is loading as fast as possible.
Sure, having a super-beefy server helps, but one important aspect of
having a fast loading website is reducing the size and number of your
page assets as much as possible. This can be a real challenge within the
current state of the Web. Web pages are becoming increasingly more
complex globs of code that require a huge amount of assets to display
and function properly. In addition to the plain old html, a ton of
javascript, css files, and small images all have to be downloaded in the
background for the page to fully render in a browser.
“What’s the big deal?” I hear you ask. “All my visitors are on
broadband, and the js/css/images are only a few KB extra – hardly a drop
in the bucket!” Now, this may very well be true. However, the fact is
that the actual size of your files are only a small part of the overall
cost incurred on a page load. There is a much more subtle bottleneck
that has nothing to do with file size: The maximum concurrent connection limit.
This is a limit the browser enforces which dictates how many
connections can be open simultaneously to a single server. Even if
you’re on a super-fast connection, your browser will still limit the
maximum number of files you can download at one time. This number varies
from browser to browser, and may change slightly depending on connection
speed and web server configuration. The actual values for Internet
Explorer, Firefox, and Chrome are below:
- IE 7 and below: 2 – 4
- IE 8 and above: 6 – 4
- Firefox 3: 6
- Firefox 3 and above: 15
- Chrome: 6
Combine and Minify
It can be helpful to think of a concurrent concurrent limit as the end
of a funnel that your page assets pour through. Naturally, the more
assets you have the longer it takes for them to get through the funnel.
Making your assets smaller helps them pour through faster, but still,
only so many can go through at once no matter how small they are. The
key is to combine them into as few files as possible, thereby reducing
the connection limit bottleneck. Making your files smaller AND combining
them is a win-win situation. Smaller files + fewer connections =
faster loading site.
Minification
To “minify” a file simply means to strip out all the “human readable”
parts of the file such as indentation, line breaks, comments,
extraneous whitespace, long variable names, etc. E.g all the stuff that
makes it easy for a human to read, but which a computer couldn’t care
less about.
Consider the following snippet of JavaScript before being minified:
/*
* test.js
* Created on Sep 9, 2012 9:58:14 PM by bob
*/
function sayHello() {
alert("Hello, World!");
}
function sayGoodbye() {
alert("Goodbye, World!");
}
/**
* This function answers the ultimate question.
*
* Just returns 42.
*
* @return integer
*/
function computeMeaningOfLife() {
//Initialize this variable to zero, for no apparent reason.
var theMeaningOfLife = 0;
//do something pointless
for(var someLongVarName = 1; someLongVarName <= 42; someLongVarName++) {
theMeaningOfLife = someLongVarName;
}
//hur huurr!
return theMeaningOfLife;
}
And now after being minified:
function sayHello(){alert("Hello, World!")}function sayGoodbye(){alert("Goodbye, World!")}function computeMeaningOfLife(){var a=0;for(var b=1;b<=42;b++){a=b}return a}
As you can see, all the comments and whitespace have been removed, in
addition descriptive variable names have been changed to a single
character in length. Just these few changes have reduced the file size
by 467 bytes.
Before Minify: 639 bytes
After Minify:
172 bytes
Similarly, CSS can be minified this way as well.
When dealing with CSS, any images referenced in a CSS file
can be inlined. The raw base64 encoded data of the image actually
becomes a part the CSS file. This reduces the number of
requests/connections even further as the images no longer have to be
fetched separately. This is accomplished through the data
uri scheme.
To illustrate, consider a typical css background image such as:
body {
background: url('images/bg.jpg');
}
The problem with the snippet above is that the file bg.jpg will cause an
extra connection to be opened in order to fetch it. By inlining the
image we can avoid this completely:
body {
background: url('data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC');
}
There are, however, a few things to keep in mind when inlining images:
- Older browsers such as IE 7 and below do not support it.
- IE 8 only supports inlined images up to 32 KB in size
- Every time an image is changed, the inline data has to be re-encoded.
The base64 encoding is slightly larger than the actual image file.
However, you are avoiding costly connections and the overhead of
additional http headers for each image, so the increased size might be
negligible depending on the images.
Combining Files
In addition to reducing the file size via minification, it's a good
practice to combine multiple files into a single file in order to reduce
the number of connections that must be opened. For example, let's
assume that your site is constructed in a modular fashion. You will
likely have separate css file for certain elements on your pages, and
various JavaScript files for different functional aspects of the page.
While it is a good idea to separate your code this way during
development, the trade off is that, in a production environment, your
visitors run up against the browser's connection limit. To
get around this, you'll need to combine the multiple files into as few
files as possible.
By both reducing the file size and combining the files together, we get
two major advantages – a much smaller payload, and fewer costly
connections that must be opened. This is a huge win toward optimizing
your website.
Tools
So how exactly do you get the files minified and combined together? It's
an important question, and one that must be considered very carefully.
Clearly you do not want to develop your site by writing unreadable
minified code in a single gigantic file, nor do you want to worry about
re-optimizing your assets after every change you make. The answer to
this question is very open-ended and far outside the scope of this blog
post, so I will just say “it depends.” In a typical development
life-cycle there will be a “build to production phase” where the
minification + combination occurs through an automated build tool of
some kind. So, your development version contains the un-minified source
code in multiple files, and your production version, after emerging
through the build tool, is ready to roll with the optimized assets. But
again, it depends on your site. I will however list a couple useful
tools that are easy to use and work well:
WP Minify
WP Minify
is a handy plugin for WordPress sites. It minifies and combines CSS and
JavaScript files. In addition, it also minifies the site's html.
PHP Sprockets
PHP Sprockets is a small general purpose php library that can minify and
combine page assets on the fly. (It's a php port of the popular Ruby
“Sprockets” library.)
With a simple url rewrite, you can run all CSS and Javascript files
through it in real-time as they are requested. This totally eliminates
having to worry about “re-processing” your assets after every change.
Additionally, it includes a “require” facility for requiring files that
depend on each other. This allows you to keep your code organized in
separate files and makes combining them easy as PHP Sprockets does all
the work of combining the files together by looking at which files
depend on each other.
I've forked PHP Sprockets and added the ability to inline images in CSS
files. You can grab it from my
github repository.
Divide and Conquer
Another way to combat the connection limit bottleneck is to divide
various page assets across separate domains. The browser connection
limit is only enforced per server. For example, suppose your site is
hosted on www.example.com. By hosting your css/javascript files on
assets.example.com, and your images on images.example.com you have
effectively tripled the number of concurrent connections a visitor your
site may open.
By using a content delivery network, such as Amazon CloudFront, you not
only gain this advantage, but also benefit from a network of
geographically distributed edge servers which ensure that all
connections made to your site take the shortest trip possible.
