How to compress .php, .css and .js files without mod_gzip or mod_deflate

File compression is possible on Apache web hosts that do not have mod_gzip or mod_deflate enabled, and it’s easier than you might think.

A great explanation of why compression helps the web deliver a better user experience is at betterexplained.com.

Two authoritative articles on the subject are Google’s Performance Best Practices documentation | Enable compression and Yahoo’s Best Practices for Speeding Up Your Web Site | Gzip Components.

Compressing PHP files

If your Apache server does not have mod_gzip or mod_deflate enabled (Godaddy and JustHost shared hosting, for example), you can use PHP to compress pages on-the-fly. This is still preferable to sending uncompressed files to the browser, so don’t worry about the additional work the server has to do to compress the files at each request.

Option 1: PHP.INI using zlib.output_compression

The zlib extension can be used to transparently compress PHP pages on-the-fly if the browser sends an “Accept-Encoding: gzip” or “deflate” header. Compression with zlib.output_compression seems to be disabled on most hosts by default, but can be enabled with a custom php.ini file:

[PHP]

zlib.output_compression = On

Credit: http://php.net/manual/en/zlib.configuration.php

Check with your host for instructions on how to implement this, and whether you need a php.ini file in each directory.

Option 2: PHP using ob_gzhandler

If your host does not allow custom php.ini files, you can add the following line of code to the top of your PHP pages, above the DOCTYPE declaration or first line of output:

<?php if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler"); else ob_start(); ?>

Credit: GoDaddy.com

For WordPress sites, this code would be added to the top of the theme’s header.php file.

According to php.net, using zlib.output_compression is preferred over ob_gzhandler().

For WordPress or other CMS sites, an advantage of zlib.output_compression over the ob_gzhandler method is that all of the .php pages served will be compressed, not just those that contain the global include (eg.: header.php, etc.).

Running both ob_gzhandler and zlib.output_compression at the same time will throw a warning, similar to:

Warning: ob_start() [ref.outcontrol]: output handler ‘ob_gzhandler’ conflicts with ‘zlib output compression’ in /home/path/public_html/ardamis.com/wp-content/themes/mytheme/header.php on line 7

Compressing CSS and JavaScript files

Because the on-the-fly methods above only work for PHP pages, you’ll need something else to compress CSS and JavaScript files. Furthermore, these files typically don’t change, so there isn’t a need to compress them at each request. A better method is to serve pre-compressed versions of these files. I’ll describe a few different ways to do this, but in both cases, you’ll need to add some lines to your .htaccess file to send user agents the gzipped versions if they support the encoding. This requires that Apache’s mod_rewrite be enabled (and I think it’s almost universally enabled).

Option 1: Compress locally and upload

CSS and JavaScript files can be gzipped on the workstation, then uploaded along with the uncompressed files. Use a utility like 7-Zip (quite possibly the best compression software around, and it’s free) to compress the CSS and JavaScript files using the gzip format (with extension *.gz), then upload them to your server.

For Windows users, here is a handy command to compress all the .css and .js files in the current directory and all sub directories (adjust the path to the 7-Zip executable, 7z.exe, as necessary):

for /R %i in (*.css *.js) do "C:\Program Files (x86)\7-Zip\7z.exe" a -tgzip "%i.gz" "%i" -mx9

Note that the above command is to be run from the command line. The batch file equivalent would be:

for /R %%i in (*.css *.js) do "C:\Program Files (x86)\7-Zip\7z.exe" a -tgzip "%%i.gz" "%%i" -mx9

Option 2: Compress on the server

If you have shell access, you can run a command to create a gzip copy of each CSS and JavaScript file on your site (or, if you are developing on Linux, you can run it locally):

find . -regex ".*\(css\|js\)$" -exec bash -c 'echo Compressing "{}" && gzip -c --best "{}" > "{}.gz"' \;

This may be a bit too technical for many people, but is also much more convenient. It is particularly useful when you need to compress a large number of files (as in the case of a WordPress installation with multiple plugins). Remember to run it every time you automatically update WordPress, your theme, or any plugins, so as to replace the gzip’d versions of any updated CSS and JavaScript files.

The .htaccess (for both options)

Add the following lines to your .htaccess file to identify the user agents that can accept the gzip encoded versions of these files.

<files *.js.gz>
  AddType "text/javascript" .gz
  AddEncoding gzip .gz
</files>
<files *.css.gz>
  AddType "text/css" .gz
  AddEncoding gzip .gz
</files>
RewriteEngine on
#Check to see if browser can accept gzip files.
ReWriteCond %{HTTP:accept-encoding} gzip
RewriteCond %{HTTP_USER_AGENT} !Safari
#make sure there's no trailing .gz on the url
ReWriteCond %{REQUEST_FILENAME} !^.+\.gz$
#check to see if a .gz version of the file exists.
RewriteCond %{REQUEST_FILENAME}.gz -f
#All conditions met so add .gz to URL filename (invisibly)
RewriteRule ^(.+) $1.gz [QSA,L]

Credit: opensourcetutor.com

I’m not sure it’s still necessary to exclude Safari.

For added benefit, minify the CSS and JavaScript files before gzipping them. Google’s excellent Page Speed Firefox/Firebug Add-on makes this very easy. Yahoo’s YUI Compressor is also quite good.

Verify that your content is being compressed

Use the nifty Web Page Content Compression Verification tool at http://www.whatsmyip.org/http_compression/ to confirm that your server is sending the compressed files.

Speed up page load times for returning visitors

Compression is only part of the story. In order to further speed page load times for your returning visitors, you will want to send the correct headers to leverage browser caching.

15 thoughts on “How to compress .php, .css and .js files without mod_gzip or mod_deflate

  1. Pavel

    I was trying to implement the solution for gzipping css and js files on godaddy, and it worked (gzipped file was served instead of normal), except for one problem: Firefox / chrome could not decompress the files outputting half-decompressed contents, so js stopped working completely. I used 7zip compressor (Gzip, method: deflate) to compress the files. I was so happy to see my jquery 1.3 downloading as 18kb file, but then disappointed to see js not working at all. Any idea why this could be happening?

    The site is on shared hosting account with goDaddy, so there is no other option for compression js files.

  2. Raj Karun

    Hi I read several blogs and articles and this one actually solved my problem.
    There were actually 40 .js files in my web directory and used 7zip and the htaccess code its working great. I had a doubt while going through the code. It is very clearly explained in the htaccess code that we first check if the browser accepts Gzip file and then we send the Gzip file, in that case why should we specifically exclude browsers. Sorry if this is a stupid/blunt question.
    Raj

  3. Steupz

    That GoDaddy gzip code never works on my site. Serves up a page with text code. Might be a plugin conflict.

  4. Eshban Bahadur

    The rule:
    ReWriteCond %{HTTP:accept-encoding} gzip

    not works on https pages. I have even test it by changing HTTP to HTTPS but nothing works. Any suggestion?

  5. Graphics Cove

    Unfortunately I can’t get these to work on the host I’m using (Which has been known to have some serious issues) but there are some things here I can use to look for alternatives. Thanks for the information.

    – Steven Noble
    Graphics Cove

  6. Patrick Janser

    Thanks a lot for the find command. I didn’t think about the trick of opening a new bash. You saved me an hour!

    Just a question about the RewriteRule line. Can’t we make it a bit more precise in order to avoid executing all the RewriteConds above?

    What about this:

    RewriteRule ^.*.(css|js)$ $0.gz [L,QSA]
    

    or better, without capturing the extension (as we don’t use it):

    RewriteRule ^.*.(?:css|js)$ $0.gz [L,QSA]
    
  7. JP

    I know this is late but maybe the Godaddy code doesn’t work because it’s malformed? This worked for me:

    if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
    ob_start("ob_gzhandler");
    } else {
    ob_start();
    }

  8. Oliver Baty Post author

    Thanks, JP.

    I think that the code got scrambled when I moved the WordPress DB from one host to another, but I’ve updated my post to use the code in your comment.

  9. Taimur

    Hi, nice and effective article. Two things:

    1) Are the methods still effective? 2014 🙂

    2_ I tried option 2 to compress files on my 1and1 linux server (debian), it returned “missing argument to ‘-exec’. I have ensured no typos, what is wrong?

    Please reply.

  10. EZ

    try this, as an alternative to “Option 2: PHP using ob_gzhandler”
    if(isset($_SERVER[‘HTTP_ACCEPT_ENCODING’]) && false !== strpos(strtolower($_SERVER[‘HTTP_ACCEPT_ENCODING’]), ‘gzip’))
    { ob_start(“ob_gzhandler”); }else{ ob_start(); }

  11. Dagon

    I have always used the htaccess code “RewriteRule ^(.+) $1.gz” to add .gz to url(s) on the fly.
    But I don’t really understand the code itself. What does “$1.gz” in the code mean?

  12. Brad Pilecki

    @Dagon,

    Here’s a quick & dirty breakdown:

    The caret, ^, signifies the start of an URL, under the current directory. This directory is whatever directory the .htaccess file is in. You’ll start almost all matches with a caret.

    The dollar sign, $, signifies the end of the string to be matched. You should add this in to stop your rules matching the first part of longer URLs. When used as $0, $1, etc, this signifies the element(s) before it to be constructed and assigned ____ value. So, basically, it’s saying grab everything from the start to the end of the string and assign that value to $1.

    The period or dot before the file extension is a special character in regular expressions. It means ANY character. Don’t confuse with (.*) which matches ANYTHING (and is a potential security hazard really).

    Now for the (+) plus sign. This modifier changes whatever comes directly before it, by saying ‘one or more of the preceding character or range.’ In this case it means that the rule will match any URL that’s defined in the .htaccess scope as a “gzip-able” file and add (.gz) to the end of it, thus parsing the file to the compressor. It also doubles as a method of avoiding blank names.

    To summarize:
    The rule | “RewriteRule ^(.+) $1.gz” is equal to the logic below:
    RewriteRule (Anything under this directory [^]) with (any character and characters preceding it [.+]) should be constructed and (assigned a .gz extensions [$1.gz])

    Hope that helps.

Comments are closed.