I was tired of waiting for this to happen. It's simple stuff and the effect is pretty big if your content primarily consists of text. Well, it's not like this affects the text - that's already compressed by Drupal if you allow it to do so - what I mean is the compression ratio. If you serve megabytes of images, sound, and video, shrinking those few percent of JS and CSS won't have much of an effect.
However, if your site is more like this one you might be able to cut the size of the site in half. For people with a non-primed cache, that is. That's a really big plus if you get a sudden surge of visits. Nowadays the typical resource usage pattern seems to go from one spike to the next with rather little usage in-between. Take a look a Esoteric Curio's excellent article if you're interested in some more details.
As I previously pointed out even your regular visitors often won't have a primed cache, because the cache is simply too small for today's bloaty web. By the time they visit your site again the cache was already overwritten most of the time. Server-sided caching and compression to the rescue!
Drupal already handles caching of pages and blocks, which already helps a lot. It's also able to utilize GZip compression for the markup, which again is cached on the server side. CSS and JS files can be also aggregated, which reduces the number of HTTP requests, which in turn also improves loading times.
However, the next logical step - compressing those aggregated CSS and JS files - was omitted. Fortunately adding this feature is very easy if you don't ever want to turn it off. Well, why would you? :)
Step 1 - Locate the following line (search for "file_save"):
file_save_data($data, $csspath .'/'. $filename, FILE_EXISTS_REPLACE);
Step 2 - Put the following line below it:
file_save_data(gzencode($data,9), $csspath .'/'. $filename . '.gz', FILE_EXISTS_REPLACE);
Step 3 - Locate the following line (search for "file_save"):
file_save_data($contents, $jspath .'/'. $filename, FILE_EXISTS_REPLACE);
Step 4 - Put the following line below it:
file_save_data(gzencode($contents,9), $jspath .'/'. $filename .'.gz', FILE_EXISTS_REPLACE);
The second parameter of gzencode is the compression level. 9 offers the best compression, but it's also the slowest. Since compression happens very rarely using a level of 9 is alright. A level of 7 or even 5 would be almost as good, while only taking a fraction of the time. But as I said, it literally runs only once in a blue moon - going for the extreme is really alright.
Keep in mind to add those two lines again after upgrading Drupal.
Add the following before the "<IfModule mod_rewrite.c>" block:
<Files *.js.gz> AddEncoding gzip .js ForceType application/x-javascript </Files> <Files *.css.gz> AddEncoding gzip .css ForceType text/css </Files>
Insert the following at the very end of the "<IfModule mod_rewrite.c>" block (before "</IfModule>"):
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)\.css $1.css.gz [L,QSA]
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)\.js $1.js.gz [L,QSA]mod_negotiate could have been used instead of mod_rewrite, but the former looked a bit less feasible from my point of view. Well, feel free to experiment with that stuff.
On my rather plain website the results are pretty impressive as illustrated by figure 1:
The document, external document, images, and the external JavaScript were already heavily compressed. Also compressing the aggregated JavaScript and CSS files saves about 28% on the front page and a whopping 44% on that specific blog entry.
This blog post uses about 30kb for additional images, which means that compression of those aggregated files saves about 34%. That's still pretty awesome, isn't it?
Update 07/10/2008: I forgot a backslash in both rewrite rules. It's now fixed in the .htaccess excerpt above. This mistake caused paths like "sh_css.min.js" to be rewritten as "sh.css.gz" instead of "sh_css.min.js.gz". Whoops. My thanks go to some random guy/gal from the Czech Republic who made it show up in the logs. :)
Comments
Awesome
This works perfect, thanks for sharing!
don't hack core
This is a lot of work when you can simply enable gzip compression in Apache.
Not always an option
With mod_deflate the content will be compressed in real time. Over and over again, which of course results in higher CPU usage. The method I'm using does that only once whenever it's necessary. The result is stored on disc and the same static file is served a zillion times.
It's a very nice solution, which really helps a lot on cheap shared hosts (such as this one).
Hacking the core is of course not desirable, but in this case the change is very simple and I'm willing to redo that as often as necessary. Drupal will have this feature in the future, but if it takes too long I might be forced to switch hosts sooner than actually necessary.
That's why I decided to do it. It also serves as a nice example of the benefits and it's also simple enough to quickly evaluate the effects on your own; everyone can do it in a couple of minutes. Oh and the diagram also makes the benefits crystal-clear.
Have an answer for 5.x?
I still am using 5.7, I would REALLY appreciate a mod for 5.7. I tried just doing this mod, but it did not all work. The files we're gzipped, but I can't figure out how to get it read it. The end result is my page without my css! Could you come up with a mod?
Hm
5.x doesn't seem to aggregate JS files, but it does aggregate CSS files at least.
Well, you shouldn't see unstyled pages ever. Therefore I'll explain briefly how it's supposed to work:
Whenever CSS or JS files are aggregated they are written to disc. This part is left unchanged. We'll still need the uncompressed files for those clients which don't support GZip compression. However, this is also the right spot to create a gzipped version for more modern clients. That's why we'll create a gzipped copy right below it.
So, now (after clearing the cached data and triggering a rebuild by requesting some pages) we should be able to see one or more aggregate files together with their gzipped copies over in the files/css or files/js directory (or wherever you told Drupal to store those files).
The actual logic is handled by .htaccess. If a CSS or JS file is requested, the request will be internally redirected to the gzipped copy. But that only happens if both rewrite conditions are met:
I forgot...
You can aggregate JS files in 5.x with the JavaScript Aggregator module.
Patch
A patch would be awesome, to maintain Drupal files after upgrades! -- Gurpartap Singh
Blank page
drupal 6.3 I get a blank page after making the above modifications
re: Blank page
Sorry for the slightly late reply. This site currently runs Drupal 6.3 and it works fine. There weren't any changes from 6.0 to 6.3 which could have broken this hack. After all it only creates a gzipped copy of those aggregated files whenever they are created.
I suggest to check your .htaccess. Re-read my instructions carefully and ensure that you really didn't do anything wrong there.
Edit: Also works with 6.4 ;)
Post new comment