There and Back Again

Compressing JavaScript and CSS

When your building fancy AJAX websites one thing that tends to happen is you end up loading amounts of JavaScript and CSS on your pages. And while browsers are smart and do a lot of client side caching you can’t get rid of that weight on your first page load.

For example on my blog I have about 60K of JavaScript and 10K of CSS. Now this isn’t horrible but when you figure images and 90K of HTML it doesn’t take long to get to my 200K total page weight.

There are a lot of various approaches for cutting down the size of JavaScript and CSS. Some of the common ones are removing whitespace and comments for JS and CSS, or using scripts which use alternate smaller syntaxes. There are a couple problems with this approach first it just doesn’t save as much space as I’d like, you can cut 60K of JavaScript down to 30K but not 10K. Second it makes debugging horrible, every JavaScript or CSS error comes from line 1 or the file.

There is a better approach and it comes from a technology in our browsers called Content Encoding. With the right headers we can send gzip’d content to the browser and it will automatically uncompress it. All modern browsers support this so its a huge win.

So now we have figure out how to compress our content, the simplest option for static files is Apache’s mod_deflate since chances are its already installed and you can enable it with a couple minutes of poking around in your httpd conf file. But it does have the disadvantage of recompressing the files for each request so it uses up some extra cpu.

mod_gzip has a similar feature set to mod_deflate with the addition of the ability to cache the compressed files but what I really want is a solution that can run on any run of the mill apache server.

To make this happen we need to compress a JavaScript file by hand and then make Apache serve it up with the correct headers.
The headers are pretty simple, Content-Type should be text/javascript and Content-Encoding should be gzip

To set the needed headers you just need to add the following rule to apache, it can go in your httpd.conf or in a .htaccess file.

<files *.js.gz>
ForceType text/javascript
Header set Content-Encoding: gzip
</files>

You can see this in action at with a small sample I made.

Now im sure your next question is what browsers support this, from my limited testing that FireFox 1.5+ and IE 6+ both work fine. From what i’ve ready anything back to version 4 of most browsers should work.

The only only downside to this approach is you have to make sure the keep your gzipped versions of files up to date and you have to update all your code to refer to the gzipped version. I don’t have a solution to the first problem, though running gzip file.js -c > file.js.gz is handy if you want to have both the uncompressed and compressed version of the file hanging around.

I thought I had a solution for the second problem using mod_rewrite. However I haven’t been able to get it working right so if you have any tips here’s my current attempt. The conditions seem to work right, but once the rediret happens the force type and content encoding header don’t seem to get set anymore.

<ifmodule mod_rewrite.c>
RewriteEngine On
RewriteBase /

RewriteCond %{HTTP_ACCEPT} >gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.+).js$ $1.js.gz [QSA,L]
</ifmodule>

Note: For testing that things are working correctly you’ll want a tool to view the headers of the http response. If you want a web based tool web-sniffer seems to work well.

22 thoughts on “Compressing JavaScript and CSS

  1. Alexander Rebholz

    Joshua,

    Thank you for your article. I would just like to add that useing gzip with Internet Explorer is not without risk e.g.:

    Content with “Content-Encoding: gzip” Is Always Cached Although You Use “Cache-Control: no-cache”
    http://support.microsoft.com/kb/321722/EN-US/

    Internet Explorer May Lose the First 2,048 Bytes of Data That Are Sent Back from a Web Server That Uses HTTP Compression
    http://support.microsoft.com/default.aspx?scid=kb;en-us;Q312496

    There are bug fixes but there might be more problems. I am not sure if gzip now works properly on IE7. So maybe one solution might be to serve uncompressed files for IE. But then probably a large amout of users wouldn’t benefit of it. It might depend on your visitors.

    regards,
    Alex

  2. Joshua Eichorn Post author

    Well this is css and js so always caching is fine.

    Also none of these problems effect upgraded ie6 or ie7 so I’m all that worried.

  3. hoffie

    You could try RewriteRule ^(.+)\.js$ $0.gz [QSA,L,T=text/javascript]
    But I don’t know a similar flag for the header line :(
    I didn’t try it, as I don’t have any apaches anymore. ;o)

  4. Travis Swicegood

    It’s slipping my mind right now, but it seems like there’s type of RewriteRule that’s a hard redirect and will force the htaccess to be reloaded. It’s not enabled by default because generally you don’t want to go over the list of mod_rewrite list again.

  5. Pingback: Sam’s random musings » Compressing JavaScript and CSS

  6. Pingback: Torrential Web Dev

  7. Joao Ricardo

    I use this simple code that i took from an article published in the Zend website:

    if (strstr($HTTP_SERVER_VARS['HTTP_ACCEPT_ENCODING'], ‘gzip’))
    {
    // Start output buffering, and register compress_output() (see below)
    ob_start(“ob_gzhandler”);

    // Tell the browser the content is compressed with gzip
    header(“Content-Encoding: gzip”);
    }

    it works just fine.

  8. Sami

    IE6 in Windows 2000 (probably in XP as well) will store gzipped files in Temporary Internet Files in gzipped format. Everything works nicely as long as Internet Explorer is not restarted.

    When Internet Explorer is finally restarted, the browser will not retrieve the .css file again from the server, but will use the file from Temporary Internet Files. For some reason, it fails to unpack the contents and is trying to use the contents of .css file in gzipped format, typically corrupting the design.

    IE7 stores gzipped content in unpacked format in Temporary Internet Files and does not have this kind of problem with .css files.

  9. Priyamvad

    Hey
    I’m getting “Header misspelled or module not included error when I try to specify the directives in my httpd.conf file. What can be the reason for it?
    how can I install mod_header for my Apache2.2.4??

    Please
    Help!

  10. brian

    The problem of setting the Content-Encoding piqued my curiosity so looked around a bit and found this page:
    http://zuble.blogspot.com/2007/02/compressed-js-and-modrewrite.html

    I made a couple of changes to his version:

    ForceType text/javascript
    Header set Content-Encoding: gzip

    RewriteEngine On
    RewriteCond %{HTTP_USER_AGENT} !”.*Safari.*”
    RewriteCond %{HTTP_ACCEPT} >gzip
    RewriteCond %{REQUEST_FILENAME}.gz -f
    RewriteRule ^(.+).js$ $1.js.gz [QSA,L]
    ForceType text/javascript

    Works for me.

  11. brian

    Ugh! The tags were eaten by your script. My last post should read:

    [FilesMatch "\.js.gz$"]
    ForceType text/javascript
    Header set Content-Encoding: gzip
    [/FilesMatch]
    [FilesMatch "\.js$"]
    RewriteEngine On
    RewriteCond %{HTTP_USER_AGENT} !”.*Safari.*”
    RewriteCond %{HTTP_ACCEPT} >gzip
    RewriteCond %{REQUEST_FILENAME}.gz -f
    RewriteRule ^(.+).js$ $1.js.gz [QSA,L]
    ForceType text/javascript
    [/FilesMatch]

    Change [ & ] to angle braces.

  12. Pingback: Inside Java2Script » Blog Archive » Compressing and Deploying JavaScript

  13. Troy

    I use…

    ForceType text/javascript
    Header set Content-Encoding: gzip

    RewriteCond %{HTTP_USER_AGENT} “MSIE 7|Firefox”
    RewriteCond %{REQUEST_FILENAME}.gz -f
    RewriteCond %{HTTP:Accept-Encoding} gzip
    RewriteRule (.*)\.js$ $1.js.gz [QSA,L]
    ForceType text/javascript

    … but it only works if the original javascript file DOESNT exist. If it does, it uses that instead. This happens on both ie7 and firefox. I don’t know why this happens but it makes it impossible to use becuase browsers other than ie7 and firefox dont support it or have bugs, and ie7 and ifrefox just use the regular file anyway. Maybe there’s something wrong with the code…

  14. Troy

    Man does mod_rewrite have bugs in it or what. I’ve been looking on the web and it appears to have loads. That line:
    RewriteCond %{HTTP_USER_AGENT} “MSIE 7|Firefox”

    …it just goes ahead and rewrites anyway regrdless of whether it matches the user agent string or not. I’ve verified this with multiple browsers and also Rex Swain HTTP viewer (http://www.rexswain.com/httpview.html). I’ve tried it in every configuration possible – it just wants to do it’s own thing.

  15. Jason S

    If you use a shared host as I do, you may have a hard time getting this version to work. I spent days on it, before I worked out another method that works.

    If your host has the php and the zlib extension you can compress your css and javascript files using an alternate method explained below.

    http://thewebdevelopmentblog.com/2008/10/tip-speed-up-your-websites-using-gzip-and-merging-files/

    It can also merge your files and cache them which will make your site load much faster again!

    Hope this helps!

  16. dbunic | www.redips.net

    Very good post. I use your method to minimize network traffic. JS and CSS files are compressed only once and served with Apache server. Today almost all browsers accept gziped content so there should not be a problem.

    Thank you!