Compressed web files are smaller and quicker to send. Drupal compresses HTML pages in its page cache, but doesn't compress uncached pages, CSS, and JavaScript. PHP output buffer compression conflicts with Drupal's compression. Instead, configure Apache's mod_gzip or mod_deflate modules.
Table of Contents
Introduction
For a small web site served through a cable modem or DSL connection, network bandwidth is limited. Compression can reduce page sizes substantially, giving visitors faster page loads, and enabling the web server to deliver more pages per second. With Drupal's page cache enabled, anonymous visitors are served compressed pages from the page cache. However, Drupal doesn't compress pages for logged-in visitors, and it doesn't compress CSS, JavaScript, XML, text, PostScript, or other text-based files. This article looks at compressing these files too by enabling compression modules for the Apache web server.
Step 1. Enable Drupal's page cache
Enable Drupal's page cache if you haven't already. For anonymous visitors this will reduce HTML page build times by 90%. Pages served from the cache are automatically compressed without any further configuration changes.
Step 2. Don't use PHP's output handler compression
PHP supports optional "obj_gzhandler" and "zlib" output handlers that can automatically compress all data sent from a PHP program. Unfortunately, both of these have problems with Drupal's page cache.
When Drupal sends a compressed page from it's page cache, it correctly marks the page with a "Content-Encoding: gzip" page header. From then on, any software that handles the page should recognize that the page is compressed. Unfortunately, both of the PHP output handlers ignore this header and mistakenly compress the output a second time. When the doubly-compressed result is shown in a web browser, it's a garbled mess.
Since PHP's output handlers can't be configured to work properly, don't use them. Disable them by editing your "php.ini" file. Set the "output_handler" line to empty, and the "zlib.output_compression" line to "Off". On a new installation of PHP, these are the default values anyway.
output_handler =
zlib.output_compression = Off
Step 3. Enable an Apache compression module
Apache has two commonly-used compression modules: mod_gzip and mod_deflate. Both can be configured to work properly with Drupal.
mod_gzip module
Mod_gzip is free and available for Apache 1.x and 2.x. For Windows PCs, download the DLL file. For Linux PCs and Macs, download the source code and compile it. Installation instructions are included with the downloads. In each case, you'll drag the new module into Apache's module folder.
Next, edit your server's "httpd.conf" file, or your web site's ".htaccess" file to enable and configure the module (replace MODULE below with the path to the installed module).
LoadModule gzip_module MODULE
AddModule mod_gzip.c
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</IfModule>
LoadModule and AddModule make the module available, and mod_gzip_on turns it on. The mod_gzip_dechunk line directs the module to collect all of Drupal's output before compressing any of it.
The mod_gzip_item_include lines list file types to compress, while mod_gzip_item_exclude lines list what not to. The last line directs mod_gzip to skip compressing files that Drupal has already compressed, such as those from it's page cache.
mod_deflate module
Mod_deflate comes pre-installed with Apache 2.x. To configure it, edit your server's "httpd.conf" file or your site's ".htaccess" file:
<Location />
SetOutputFilter DEFLATE
DeflateCompressionLevel 9
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \.(?:exe|t?gz|zip|gz2|sit|rar)$ no-gzip dont-vary
</Location>
SetOutputFilter enables the module and DeflateCompressionLevel sets the amount of compression from 1 (not much) to 9 (as much as possible). The SetEnvIfNoCase lines block the module from compressing images, executables, and previously compressed files. Everything else gets compressed.
File size results
After adding and configuring a compression module, restart Apache. To confirm that compression is working you'll need to look at the HTTP response header sent from your web server back to your browser. This header isn't normally shown by browsers, so use the free Web Sniffer web site to show the header. You want to see a line like this:
Content-Encoding: gzip
Google's free Load Time Analyzer 1.5 extension for Firefox provides an easy way to show file sizes before and after enabling compression. Load a web page, then press the extension's "Graph" button to list the files loaded by the page, their sizes, how long each one took to load, and the order in which they were loaded.
The table below lists the files downloaded for the home page of our simple and complex test web sites (see Specifications for Drupal web site testing). The complex site has more modules enabled, so it includes more CSS and image files than does the simple site.
| Simple site (bytes) | Complex site (bytes) | |||
|---|---|---|---|---|
| Original | Compressed | Original | Compressed | |
| Simple home page | 17,805 | 4,229 | ||
| Complex home page | 29,471 | 6,086 | ||
| themes/garland/style.css | 16,544 | 3,977 | 16,544 | 3,977 |
| files/color/garland-b3c7bd49/style.css | 16,437 | 3,940 | ||
| modules/system/system.css | 6,932 | 2,003 | 6,932 | 2,003 |
| modules/fvestar/theme/fivestar.css | 1,356 | 384 | ||
| themes/garland/print.css | 1,192 | 499 | 1,192 | 499 |
| modules/user/user.css | 858 | 385 | 858 | 385 |
| modules/aggregator/aggregator.css | 779 | 303 | ||
| modules/system/defaults.css | 737 | 415 | 737 | 415 |
| modules/node/node.css | 717 | 358 | 717 | 358 |
| modules/forum/forum.css | 694 | 318 | ||
| modules/tagadelic/tagadelic.css | 601 | 234 | ||
| modules/book/book.css | 571 | 260 | ||
| modules/devel/devel.css | 566 | 274 | ||
| modules/poll/poll.css | 515 | 262 | ||
| modules/cck/content.css | 391 | 391 | ||
| modules/taxonomy_context/taxonomy_context.css | 298 | 298 | ||
| TOTAL text | 44,785 | 11,866 | 78,659 | 20,387 |
| (26%) | (26%) | |||
| themes/garland/logo.png | 5,399 | 5,399 | ||
| files/color/garland-b3c7bd49/logo.png | 4,274 | 4,274 | ||
| themes/garland/images/bg-content-left.png | 3,275 | 3,275 | ||
| files/color/garland-b3c7bd49/bg-content-left.png | 2,042 | 2,042 | ||
| themes/garland/images/bg-content-right.png | 3,169 | 3,169 | ||
| files/color/garland-b3c7bd49/bg-content-right.png | 1,928 | 1,928 | ||
| misc/favicon.ico | 1,150 | 1,150 | 1,150 | 1,150 |
| misc/feed.png | 764 | 764 | 764 | 764 |
| themes/garland/images/body.png | 712 | 712 | ||
| files/color/garland-b3c7bd49/body.png | 183 | 183 | ||
| themes/garland/images/bg-content.png | 485 | 485 | ||
| files/color/garland-b3c7bd49/bg-content.png | 274 | 274 | ||
| files/images/text.png | 662 | 662 | ||
| themes/garland/images/menu-leaf.gif | 175 | 175 | ||
| files/color/garland-b3c7bd49/menu-leaf.png | 274 | 274 | ||
| files/color/garland-b3c7bd49/menu-collapsed.png | 176 | 176 | ||
| themes/garland/images/bg-navigation.png | 104 | 104 | ||
| files/color/garland-b3c7bd49/bg-navigation.png | 98 | 98 | ||
| TOTAL images | 15,233 | 15,233 | 11,825 | 11,825 |
| TOTAL | 60,018 | 27,099 | 90,484 | 32,212 |
| (45%) | (36%) | |||
Mod_gzip compression only affects text files like HTML, CSS, and JavaScript. Compression reduced text file sizes by 74%. The image files remain unchanged and are a big part of the total data downloaded for the page. Taking image files into account, text file compression reduced the total page size by 55-65%.
Load time results
Smaller files take less time to send. This is particularly important when the web site is served through a limited network connection, such as a cable modem or DSL. To measure compression's impact on load time, we simulated a cable modem's bandwidth limits by using the Charles proxy server to throttle network bandwidth down to the 64 Kbyte/sec typical of a cable modem. Loading a web page through the proxy server logs page sizes and download times. Compression reduced text file load times by 60-70%. Taking image files into account, compression reduced the total page load time by about 50%.
| Simple site (seconds) | Complex site (seconds) | |||
|---|---|---|---|---|
| Original | Compressed | Original | Compressed | |
| Simple home page | 4.538 | 1.251 | ||
| Complex home page | 7.912 | 2.866 | ||
| themes/garland/style.css | 3.157 | 0.640 | 3.157 | 0.640 |
| files/color/garland-b3c7bd49/style.css | 2.280 | 0.623 | ||
| modules/system/system.css | 1.492 | 0.591 | 1.492 | 0.591 |
| modules/fvestar/theme/fivestar.css | 0.299 | 0.178 | ||
| themes/garland/print.css | 0.276 | 0.190 | 0.276 | 0.190 |
| modules/user/user.css | 0.625 | 0.182 | 0.625 | 0.182 |
| modules/aggregator/aggregator.css | 0.225 | 0.183 | ||
| modules/system/defaults.css | 0.228 | 0.182 | 0.228 | 0.182 |
| modules/node/node.css | 0.216 | 0.173 | 0.216 | 0.173 |
| modules/forum/forum.css | 0.213 | 0.175 | ||
| modules/tagadelic/tagadelic.css | 0.204 | 0.156 | ||
| modules/book/book.css | 0.206 | 0.160 | ||
| modules/devel/devel.css | 0.199 | 0.168 | ||
| modules/poll/poll.css | 0.191 | 0.394 | ||
| modules/cck/content.css | 0.178 | 0.175 | ||
| modules/taxonomy_context/taxonomy_context.css | 0.433 | 0.171 | ||
| TOTAL text | 10.532 | 3.209 | 18.334 | 7.207 |
| (30%) | (39%) | |||
| themes/garland/logo.png | 1.760 | 1.760 | ||
| files/color/garland-b3c7bd49/logo.png | 1.062 | 1.062 | ||
| themes/garland/images/bg-content-left.png | 1.448 | 1.448 | ||
| files/color/garland-b3c7bd49/bg-content-left.png | 0.866 | 0.866 | ||
| themes/garland/images/bg-content-right.png | 0.822 | 0.822 | ||
| files/color/garland-b3c7bd49/bg-content-right.png | 0.923 | 0.923 | ||
| misc/favicon.ico | 0.267 | 0.267 | 0.267 | 0.267 |
| misc/feed.png | 0.221 | 0.221 | 0.221 | 0.221 |
| themes/garland/images/body.png | 0.220 | 0.220 | ||
| files/color/garland-b3c7bd49/body.png | 0.181 | 0.181 | ||
| themes/garland/images/bg-content.png | 0.214 | 0.214 | ||
| files/color/garland-b3c7bd49/bg-content.png | 0.181 | 0.181 | ||
| files/images/text.png | 0.208 | 0.208 | ||
| themes/garland/images/menu-leaf.gif | 0.162 | 0.162 | ||
| files/color/garland-b3c7bd49/menu-leaf.png | 0.165 | 0.165 | ||
| files/color/garland-b3c7bd49/menu-collapsed.png | 0.181 | 0.181 | ||
| themes/garland/images/bg-navigation.png | 0.145 | 0.145 | ||
| files/color/garland-b3c7bd49/bg-navigation.png | 0.173 | 0.173 | ||
| TOTAL images | 5.259 | 5.259 | 4.428 | 4.428 |
| TOTAL | 15.791 | 8.468 | 22.772 | 11.635 |
| (54%) | (51%) | |||
Conclusions
Drupal's page cache automatically compresses cached HTML pages for anonymous visitors. Enabling Apache's mod_gzip or mod_deflate modules compresses everything else, including HTML pages for logged-in visitors, as well as CSS, JavaScript, text, and other files. This reduces the total page load time by about 50%.
These tests temporarily disabled Drupal's page cache to measure size and load times of uncompressed and compressed HTML pages. Enabling the page cache shaves 1-3 seconds off the total page load time. While that is certainly good, the page load time is dominated by the time to load CSS and image files, neither of which are affected by Drupal's page cache. Also, the page cache only speeds up page delivery for anonymous visitors. Logged-in visitors will be served custom uncached pages with load times like those above.
What to do next
Measured page load times at cable modem speeds for our simple and complex test sites are still 8 and 11 seconds, respectively. This is way too high. For good usability, we need to be under 1 second.
- Compress your images. About half of the page load time measured in this article was for images. Often, careful changes to the JPEG and PNG compression settings can substantially reduce file sizes without noticeable image quality problems.
- Use a simpler theme. The default "Garland" theme in Drupal is very expensive. It's CSS and image files account for 50% of the page size and download time.
- Simplify web pages. Every image on a page takes time to download, and every Drupal block takes time to process when building a web page. Simpler pages will build and download quicker.
- Disable unused features. Every enabled Drupal module can take time when building a web page, and many modules include their own CSS and image files. Turn off unnecessary modules to lower page load times.
- Aggregate CSS files. Each file used by a web page has to be downloaded to your browser. The HTTP request and response for the file takes time. For small files, the 500 bytes of a typical HTTP response can be bigger than the file itself. Reduce this request-response overhead by combining multiple small files into one big file.
- Be browser and proxy cache friendly. Browsers and your ISP's proxy servers can cache frequently used files, but only if you enable them. Drupal's HTML pages cannot be cached since they may change for each user and visit, but it's CSS and image files can be. The default settings in Apache enable caching, but require that the cache confirm cache handling on every file request. This confirmation takes time. You can avoid this cost by enabling expiration times on cacheable files.
