Speed up a Drupal web site by enabling CSS file aggregation

Technologies: Drupal 5+

Speed up your web site by automatically combining multiple module and theme CSS files into one large file using Drupal's CSS file aggregation feature. The single large CSS file is more efficient to send to a visitor's browser, speeding up the site. The large file also has white-space removed, making it a bit smaller and faster to send. This article shows how to enable the feature and it benchmarks the performance improvement.

This article is part of the series on Essential steps to speed up a Drupal web site. Other articles in this series discuss enabling MySQL, PHP, and Drupal caching to optimize a site's theme and page layout.

How to enable Drupal CSS file aggregation

Drupal's CSS file aggregation is disabled by default. Enable it to reduce the total CSS file size and speed up page load times.

  1. Log in as your Drupal site’s administrator.
  2. Go to the Administer » Site configuration » Performance page.
  3. Check the Enabled checkbox for Aggregate and compress CSS files.
  4. Click the Save configuration button.

What does it do?

A site's theme and many of its modules each have CSS files to style content. These files take time to send to a visitor's browser, slowing down page loads. When enabled, Drupal's CSS file aggregation automatically combines all of these CSS files into one large file. As it combines them, it removes white-space (spaces, tabs, blank lines, and comments), making the data smaller. The smaller data is faster to send, speeding up the site.

While white-space removal is good, the principal speed-up comes from combining the files. Each file used by a web page has to be retrieved individually by the visitor's browser. The more files there are (CSS, JavaScript, and images), the longer this takes. CSS file aggregation combines a dozen or more small files into one large file, enabling the visitor's browser to make just one file request instead of a dozen. This speeds up page load time.

The aggregated CSS file is automatically updated if you:

  • Enable, disable, or update a module.
  • Select a different theme.

The aggregated CSS file is not automatically updated if you:

  • Edit a module’s CSS file.
  • Edit a theme’s CSS file.

To see your changes while editing a module or theme CSS file, disable aggregation. Re-enable it when you're done.

How well does it work?

I benchmarked the effect of CSS file aggregation on a test web site (see the appendix for details on how I tested). The site uses the default Garland theme and a typical set of blocks on the left and right sides of the page.

Effect on total CSS file size

The table shows the number of CSS files, and their total size before and after enabling CSS file aggregation.

Total CSS file size
(lower is better)
Test case # of files Size  
Disabled 18 15,821
Enabled 2 7,549
Reduction 89% 52%  

CSS aggregation reduced the total CSS file size by 52% from the removal of unneeded spaces, carriage-returns, and comments.

The 18 separate CSS files used by this test site were reduced to just two files. One of these two is the aggregated CSS file. The other file is one added by the "Garland" theme by using a CSS "@import" statement, which bypasses Drupal's aggregation. If the web site had used a theme that never used "@import", there would have been just one CSS file after aggregation.

Effect on total CSS load time

I measured the time to load all of the site's CSS files while simulating two network bandwidths:

  • 64 Kbps: Home and small businesses may elect to host their web site on a spare PC on a cable modem or DSL connection. While these connections are great for browsing web sites at home, they have fairly slow upload speeds for sending data back out to the Internet. Your web server's delivery speed is limited to the 64 Kbps (kilobits per second) typical of these types of connections (though they've been getting faster recently).
  • 6 Mbps: Larger business sites served by IT departments, ISPs, or web hosting services use commercial Internet connections that are much faster than a cable modem or DSL connection. When served over a commercial connection, your server's delivery speed is now limited by the slower connections of your visitors. A typical cable modem or DSL connection is around 6 Mbps (megabits per second).
CSS load time at 64 Kbps
(lower is better)
Test case Time (ms)  
Disabled 4,473
Enabled 1,678
Reduction 63%  
CSS load time at 6 Mbps
(lower is better)
Test case Time (ms)  
Disabled 1,553
Enabled 196
Reduction 87%  

CSS aggregation improved CSS load times by 63-87%. The aggregated CSS file with white-space removed is faster to send to your site visitors.

As network bandwidth goes up, the time to send the data goes down, of course. But whether you're on a fast or slow network connection, there is always about the same amount of latency or delay time between asking for a file and receiving that file. On a slow connection, that latency is a minor factor compared to the slowness of the connection. But on a fast connection, latency becomes a dominant factor. The more files your web page needs from the server, the more file requests the browser must issue and the more time is spent waiting because of network latency. Since you can't make the network latency go away, your best fix is to reduce the number of files you need per page. This is the principal benefit of CSS file aggregation.

Effect on total page load time

I measured the total page load time for the test site's home page. This time includes the CSS plus the HTML, images, and JavaScript needed by the page.

Page load time at 64 Kbps
(lower is better)
Test case Time (ms)  
Disabled 6,832
Enabled 4,017
Reduction 41%  
Page load time at 6 Mbps
(lower is better)
Test case Time (ms)  
Disabled 2,101
Enabled 593
Reduction 72%  

CSS files can be a substantial part of the data your web server must send to every site visitor. Aggregating and optimizing CSS files, while leaving the rest of the site unchanged, reduced the page load time of this site by 41-72%.

When doesn't it work?

There are a few rare quirks:
  • If a CSS file uses a full path in a URL (e.g. “background-image: url(/a/b/image.jpg) no-repeat top left”), the URL will be improperly handled in the aggregated file and the image will not be found. Avoid this problem by moving the image into the same folder as the CSS file, then use a relative path (e.g. “background-image: url(image.jpg) no-repeat top left”).
  • If a CSS file imports a second CSS file (e.g. “@import second.css”), only the import statement is added to the aggregated CSS file, not the entire second file. When the page is loaded, the browser will import the second file and the site will look fine. However, the second file doesn't benefit from white-space removal. Avoid this by putting all the CSS for a module or theme into one file; don’t use imports.
  • If a module changes its CSS file from page to page, it must notify Drupal not to aggregate its CSS file. If it doesn't tell Drupal, the module’s CSS changing will break. This is a bug in the module, so check for a newer version with the bug fixed.

Conclusions

Drupal has a main style sheet. Each module may add its own style sheet. And your site's theme has one or more style sheets. For a complex site, these style sheets can be large. Combining them with Drupal's CSS file aggregation feature reduces their size and creates just one file to download, instead of many. This is such a clear win that every site should enable aggregation before it goes live.

Further reading

Appendix: How I tested

All testing used a Drupal 5.1 web site loaded with sample content generated by the Devel module's generator scripts. The site used the Garland theme and enabled Drupal's page caching, MySQL's query caching, and eAccelerator's compiled PHP script caching. Apache was configured to compress all CSS, JavaScript, and HTML files.

File sizes and page download times were measured using the Charles proxy server to monitor a page loaded into Firefox on a Mac. Charles also provided bandwidth throttling to simulate connections at 64 Kbps and 6 Mbps. To discount variations in network and server load, the same page was loaded repeatedly until the standard deviation between the five best load times dropped below a threshold.

As with all benchmarking, your results will vary due to differences in web server CPU speed, memory size, and web site attributes, such as your choice of theme and modules. Use this article's results only as a rough guideline.

Comments

Thx!

Hi,
I am looking for the opportunity to improve the performance for drupal. Thanks for tips :)

Whoohoo! My JS aggregation

Whoohoo! My JS aggregation patch has finally made it into core to join my other CSS preprocessor patch :-)

With both of those in, is there anything else we can do to still improve the speed and loading time of CSS and JS in Drupal?

More ways to improve CSS/JS loading time in Drupal

CSS aggregation combines CSS files and removes white-space. These are both very effective. It is tempting to go through a lot more effort to optimize CSS further by remove redundant selectors and properties and converting CSS long-hand properties to shorthands. There are multiple CSS optimizers available to do this, but it's not worth the effort. The reduction in CSS file size does not produce a noticeable improvement on page load times. See my article for more on this topic: Don't bother using CSS optimization to speed up a web site.

Along the same lines, its tempting to use HTML optimizer products to remove white-space from HTML to make it smaller and faster to send. Again, this has no noticeable improvement on page load times. See my article for more on this too: Don't bother using HTML optimization to speed up a web site.

So what does work? Today's page load times are primarily limited by network latency, and not network bandwidth. Network latency is the time it takes a browser's file request to go across the network to the server, and the server's response to come back. Today, browsers spend most of their time waiting for content to arrive. Making files smaller won't help because it doesn't affect that wait time. Network latency is primarily a function of the distance from the user to the server, and the speed of light. You can't change the speed of light, of course, but using a Content Distribution Network (CDN) can copy your content to a closer location and reduce the wait time. And that's about it.

Given the unavoidable fact of network latency, the best thing you can do is to reduce the number of separate file requests made to build a page. CSS and JavaScript aggregation do this, well. But what else could you do along these lines?

  • Embed CSS and JavaScript in the HTML itself. Google, Yahoo!, and Windows Live do this now for their home pages. If we assume typical USA broadband users with a 1 Mbyte/second download speed and 1/10-th of a second of latency, then embedding CSS and JavaScript in the HTML is a performance win when those are smaller than 100 Kbytes. Most CSS and JavaScript files are. A future Drupal module could do this automatically.
  • Embed small images in the HTML as data images. All browsers except Internet Explorer (as usual) support data URLs in images. This allows small images to be encoded and placed directly in the HTML, saving one file request at the cost of making the HTML bigger. As above, if the encoded image data is smaller than 100 Kbytes, it's a win. Using browser detection, Internet Explorer users would get a normal page while everybody else would get a faster page with embedded image data. Again, a future Drupal module could do this automatically.

The above could be done now. To optimize further, we really need to stop sending web pages as a long stream of individual files. The file request time is swamping the download time. Instead, we need to create new web standards for page archives that contain a bundle of page files in one file. The starting point for such a standard already exists: MHTML. This is a bundling format used for HTML email that combines the HTML message and images into a single email-able file. Something similar could be done for page archives. Such archives could be based upon ZIP together with a manifest with instructions for building the page.

Page archives don't exist yet but they are a likely solution to the network latency problem. Until then, tricky AJAX code could achieve something similar by downloading and inserting page content as a large bundle instead of using multiple smaller requests.

Meanwhile, the best thing a site designer can do to improve site performance is to reduce the number of files per page. Cut out non-vital images for fancy bullets, background textures, icons, and logos. Aggregate CSS and JavaScript and avoid Flash and Silverlight.

A note for the unwary...

Another thing that will cause this to fail (as I've just discovered the hard way) is using absolute paths in your drupal_add_css calls.

So the following will not work:

drupal_add_css("/modules/mymodule/mymodule.css");

While the following will:

drupal_add_css(drupal_get_path("module","mymodule")."/mymodule.css");

By the way, the style sheet filename doesn't have to be the same as the module name, but it's a good convention. If you have a huge module like I have on www.bachtrack.com, then you can use more than one style sheet just to break it up.

David this post has over 2

David this post has over 2 years, still useful though (so thanks anyway =)

Very helpful post!

I'm trying to fix an aggregation issue on my drupal 7 installation and this post is still helpful! A background-image wasn't showing and now I know why! Thanks!

-- Scott

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

Nadeau software consulting
Nadeau software consulting