Serving pre-compressed js and css when mod_deflate is not available or not desirable.

Serving compressed static contents like javascript and CSS files has become a must-have for every modern website. It helps mitigating the growing size of client side scripts in the modern Web 2.0 pages. By compressing those resources you get a number of benefits - faster page load time, less traffic consumption and hosting price.


mod_deflate


Apache httpd provides convenient and easy to use mod_deflate. This module performs on-the-fly compression on selected resources. The setup is fairly easy - you just add a few line to your htaccess file:

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE application/javascript text/css
</IfModule>

The catch here is that this causes compressing the resources on ERVERY request. This causes significant CPU consumption on the busy websites and is the primary reason this module is not enabled on shared hostings.


The workaround
 


Not having mod_deflate does not mean that you can't serve compressed resources. Here's the idea: what if you compress all css and js files and than just serve the compressed contents. First let's assume that you have a compressed copy for the files - script.js has compressed script.jsgz and style.css has its style.cssgz. With the following rewrite rules you serve the compressed content to all clients that accept it. Also remember to tune the content type, because the server doesn't know about the content type of .cssgz and .jsgz files.

RewriteEngine on

RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule (.*)\.css$ $1\.cssgz [L]

AddType "text/css;charset=UTF-8" .cssgz
AddEncoding gzip .cssgz

RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule (.*)\.js$ $1\.jsgz [L]

AddType "text/javascript;charset=UTF-8" .jsgz
AddEncoding gzip .jsgz

Automating creation of compressed versions
 


To automate creation of compressed versions use the following script. Just set it to run every hour or so in the crontab:

bash update-compressed-content.sh ~/public_html '*.css'
bash update-compressed-content.sh ~/public_html '*.js'

update-compressed-content.sh:


#!/bin/bash

dir=$1
pattern=$2
test_run=$3

exec_line() {
    cmd="$1"
    do_not_run="$2"
   
    echo "Executing $cmd";
    if [ "X" == "X$do_not_run" ]
    then
        out= eval $1
        echo "Result is $out"
    fi
}

if [ -z "$dir" ] || [ -z "$pattern" ]
then
    echo "Usage: $0 <directory> <pattern>"
    exit 1
fi

echo "find \"$dir\" -name \"$pattern\""
find "$dir" -name "$pattern" | while read fileto
do
    echo $fileto
    if [ -e "${fileto}gz" ] && [ "${fileto}gz" -nt "$fileto" ]
    then
        echo "Compressed content for $fileto is up to date"
    else
        echo "Compressing $fileto"
        exec_line "cat '$fileto' | gzip > '${fileto}gz'" $test_run
    fi

done

 

No comments yet

Back to articles list

This page was last modified on 2017-10-20 15:40:01