Friday, February 15, 2008

mod_gzip for apache. Compiling, installing, and configuring how-to

So you want to gzip your css and javascript files to save bandwidth? Here is a short guide that shows you how to do just that. You can also gzip .html files if you have them.

mod_gzip is popular module for the apache webserver. It is quite possible that your host already has it installed, however, if they do not, you can point them to this page and say you would like it installed. Usually you can find out by checking the server's "Server" response header. PHPwned.org's server header:
 
Code:
Server: Apache/1.3.41 (Unix) mod_gzip/1.3.26.1a PHP/5.2.6

If you have root access, you can always (re)compile apache with mod_gzip. You can enable it as a shared module (.so, loaded at runtime, does not require a recompile of apache, uses apxs to configure) but for this how-to I am going to show you how to compile it as a static module, as it performs better this way.

First, grab a copy of mod_gzip. You can get it from http://sf.net/projects/mod_gzip/ and click "downloads"

Get the one that ends in .tgz and download it /usr/local/src

Get the latest stable version of apache from http://httpd.apache.org/ and also download it to /usr/local/src. For this how-to I am using apache-1.3.41 but if you prefer apache 2.x then use that, compiling should be the same.

Next, untar apache, and mod_gzip. If you are using apache 2.x it will replace apache* with httpd* below
 
Bash Code:
tar zfxv ./apache*.tar.gz
tar zfxv ./mod_gzip*.tgz

Chand directory to the newly created apache directory. Once there, create a directory src/modules/gzip. If you are using apache 2.x replace apache* with httpd* below.
 
Bash Code:
cd ./apache*
mkdir src/modules/gzip

Now that we created a directory for the gzip module, we need to copy the needed mod_gzip source files to that directory. We need to copy all .c .h and .tmpl files from mod_gzip directory to our gzip module directory
 
Bash Code:
cp ../mod_gzip*/*.c src/modules/gzip
cp ../mod_gzip*/*.h src/modules/gzip
cp ../mod_gzip*/*.tmpl src/modules/gzip

Now we are ready to configure the apache build. You also need to tell apache to include the gzip module. So ./configure on apache just as you normally would, with any additional options to suit your needs. In this example, I am enabling mod_rewrite, as well as mod_so (so even though we are compiling only static modules, we can compile php as a shared module, if you do not compile with mod_so you will not be able to use shared modules unless you compile any apache module as a shared module then mod_so is automatically included)
 
Bash Code:
 
./configure --enable-module=rewrite --enable-module=so --activate-module=src/modules/gzip/mod_gzip.c
 

Once configure completes, run make. Set the enviroment variable LIBEXT to "a" because for some reason mod_gzip doesn't detect this automatically. That was my experience anyway.
 
Bash Code:
 
LIBEXT=a make
 

Now its time to install, run "make install" as root. You can do this with "sudo" if you are in sudoers (usually if you are in the "wheel" group), or you can "su root" before running "make install"
 
Bash Code:
 
sudo make install
 

If you did not set --prefix when you configured apache, apache will now be in /usr/local/apache

Now start apache via apachectl as root
 
Bash Code:
 
sudo /usr/local/apache/bin/apachectl start
 

Now your "Server" response header should include mod_gzip. It is time to add or edit .htaccess file to gzip output on certain files. PHPwned.org has seperate folders for css and javascript called "css" and "js". So I only added .htaccess in each folder because I didnt not need to gzip anything in any other directory. If you do not have seperate folders, then add the following to .htaccess in your document root, combining the configurations (removing duplicate directives.

my .htaccess for my css directory (css/.htaccess)
 
Apache config / .htaccess Code:
<IfModule mod_gzip.c>   # only if mod_gzip is loaded
        mod_gzip_on Yes   # then enable mod_gzip
        mod_gzip_can_negotiate Yes # this will find files with .gz extension matching the same filename. So if you request global.css and global.css.gz exists and is newer, it does not gzip global.css, instead it just sends the .gz, saving cpu time
        mod_gzip_static_suffix .gz # the gzip extension
        AddEncoding gzip .gz  # make sure apache sends "Content-Encoding: gzip" in the response
        mod_gzip_update_static Yes # if global.css is newer than global.css.gz then it gzip global.css and overwrites global.css.gz (global.css.gz must be writable (chmod 777))
        mod_gzip_temp_dir /tmp/mod_gzip # mod_gzip temp dir
        mod_gzip_handle_methods GET # dont handle POST
        mod_gzip_item_include file \.css$ # gzip files ending in .css
        mod_gzip_item_include file \.htc$ # gzip files ending in .htc
</IfModule>

Copy the above file for js and replace .css with .js and take out the .htc line. If you are enabling it for your entire webroot, copy the .htc line and replace .htc with .js on the copied line and place the .htaccess in your document root instead of in seperate directories.

For mod_gzip to work, the temp_dir MUST exist. If not, requests that have gzipped files (or set to be gzipped) will return empty content. So create the tmp_dir
 
Bash Code:
mkdir /tmp/mod_gzip
chmod 777 /tmp/mod_gzip

To save the server's resources, lets create .gz files for each file we want to be gzipped. I created a little bash script to do this for me. I also added an additional entry for css/hover.htc which you might not have so you can remove the last 3 lines if you don't have this. If your files are in your document root instead of seperate directories, change "for f in $ext/*.$ext" to "for f in ./*.$ext"
 
Bash Code:
#!/bin/bash
DODIRS=('css' 'js')
for ext in ${DODIRS[@]}
do
        for f in $ext/*.$ext
        do
                gzip -c -9 $f > $f.gz
                echo "gzipped $f        to $f.gz"
        done
done
f="css/hover.htc"
gzip -c -9 $f > $f.gz
echo "gzipped $f        to $f.gz"

* Name this gzip.sh and put it in your document root. Every time you make changes to any of your css or javascript files, run it.
 
Bash Code:
 
sh gzip.sh
 

To make sure your server is sending the gzipped content, go to http://your.site/css/global.css (or whatever file you want to send gzipped) and check the response headers. There should be a header "Content-encoding: gzip". If not, mod_gzip is not configured correctly.

If you have any questions, comments, suggestions, need help, or just want to chat, please register and leave a comment below (if it is not related to this tutorial, leave a comment on my profile instead). You can also instant message me. My screennames are on my profile

No comments: