Faster Page Loads – Bundle Your CSS and Javascript
Have you ever watched your status bar while you wait for a page to load and wondered why several files seem to be downloaded before you see anything at all on your screen? Eventually the page content displays, and then the images are slotted in.
The files that keep you waiting are generally the CSS and Javascript files linked to from the “head” section of the HTML document. Because these files determine how the page will be displayed, rendering is delayed until they are completely downloaded.
HTTP Overhead
For each of these files, an HTTP request is sent to the server, and then the browser awaits a response before requesting the next file. Limits (or limitations) of the browser generally prevent parallel downloads. This means that for each file, you wait for the request to reach the server, the server to process the request, and the reply (including the file content itself) to reach you. Put end to end, a few of these can make a big difference to page load times.
Example Case
As a demonstration, I’ve created an empty HTML document which loads 5 stylesheets. Browser cache is disabled, and I’ve used Firebug, an invaluable tool for any web developer, to visualise the HTTP timing information. Oh, and I’ve omitted the DOCTYPE declaration for brevity only – no real sites were harmed in the writing of this article.
<html>
<head>
<title>Apache / PHP</title>
<link rel="stylesheet" type="text/css" href="https://www.sitepoint.com/css2/structure.css" />
<link rel="stylesheet" type="text/css" href="https://www.sitepoint.com/css2/standard.css" />
<link rel="stylesheet" type="text/css" href="https://www.sitepoint.com/css2/format.css" />
<link rel="stylesheet" type="text/css" href="https://www.sitepoint.com/css2/index.css" />
<link rel="stylesheet" type="text/css" href="https://www.sitepoint.com/css2/promo.css" />
</head>
<body>
</body>
</html>
A total load time of 2.24 seconds. For an empty page.
Lets see what happens if we request the same stylesheets from lighttpd, a lighter weight web server which is not being weighed down by PHP. It’s worth noting that the use of a separate domain for static content means any cookies set on the sitepoint.com domain will not be sent with the HTTP requests. Smaller requests are faster requests.
<html>
<head>
<title>lighttpd</title>
<link rel="stylesheet" type="text/css" href="https://i2.sitepoint.com/css2/structure.css" />
<link rel="stylesheet" type="text/css" href="https://i2.sitepoint.com/css2/standard.css" />
<link rel="stylesheet" type="text/css" href="https://i2.sitepoint.com/css2/format.css" />
<link rel="stylesheet" type="text/css" href="https://i2.sitepoint.com/css2/index.css" />
<link rel="stylesheet" type="text/css" href="https://i2.sitepoint.com/css2/promo.css" />
</head>
<body>
</body>
</html>
We’ve shaved off half a second, but I suspect the bulk of the time is spent going back and forth to the server for each file. So how do we improve the load time further?
Static Content Bundling
Maintaining small, modular stylesheets can make development easier, and make it possible to deliver styles specifically to the pages that require them, reducing the total CSS served per page. But we are seeing now that there’s a price to pay in the HTTP overhead.
This is where you gain performance from server side bundling of static content. Put simply, you make a single request to the server for all the CSS files required for a page, and the server combines them into a single file before serving them up. So you still upload your small, modular stylesheets, and the server is capable of delivering any combination of them as a single file.
Using the technique described in an article at rakaz.nl (PHP script also available there), we can handle special URLs that look like this:
http://example.org/bundle/one.css,two.css,three.css
For SitePoint.com, I wrote a bundling implementation in Lua for lighttpd/mod_magnet so that we can still avoid the overhead of Apache/PHP. When a combination of files is bundled for the first time, the bundle is cached on the server so that it only needs to be regenerated if one of the source files is modified. If the browser supports compression, lighttpd/mod_compress handles the gzipping and caching of the compressed version.
Lets check our load time now..
<html>
<head>
<title>lighttpd bundled</title>
<link rel="stylesheet" type="text/css" href="https://i2.sitepoint.com/bundle/structure.css,standard.css,format.css,index.css,promo.css" />
</head>
<body>
</body>
</html>
The Final Result
560ms! That’s 1.6 seconds quicker than the original example.
This bundling technique works for CSS and also JavaScript.. but obviously you cannot mix the two in a single bundle.