Delivering Pre-Compressed (gzip) Javascript with PHP only

There are plenty of results in Google for delivering compressed Javascript files using PHP.  However, most of those techniques involve compressing the JS file(s) on the fly.

I was looking for a method that would meet the following criteria:

  • The JS file should be pre-compressed using gzip.
  • The gzip version of the JS file should only be delivered if the client’s browser supports gzip.

The code below has some pitfalls:

  • It will not work with a CDN or any external content server that does not support PHP.
  • It is more difficult to maintain than compressing on the fly at the web server level (as you must maintain the compressed version of the JS file.

I’ll use my quick and dirty group chat site, chmoot.com, as an example.  I have one main JS file that handles all client-side code for the entire site.  The file is 55KB uncompressed, and 11KB with maximum gzip compression (a significant savings!)

I have a header file that is included with every page which contains:

<?php if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) { ?>
    <script type="text/javascript" src="/static/lib/chmoot.js.php"></script>
<?php } else { ?>
    <script type="text/javascript" src="/static/lib/chmoot.js"></script>
<?php } ?>

The first line checks to see if the client can accept gzip encoded files.  The first script tag points to a .php file instead of the regular .js file, as the .php file will deliver the compressed Javascript with the correct headers.  The second script tag is the fallback case to deliver the uncompressed .js file.

I also wrote the .php file called chmoot.js.php:

<?php
header('Content-encoding: gzip');
header('Content-Type: text/javascript');
header('Cache-Control: max-age=86400, max-age=86400');
header('Expires: ' . date('r', time() + 86400));
include("$_SERVER[DOCUMENT_ROOT]static/lib/chmoot.js.gz");
?>

The first line sets the content encoding to gzip, which tells the browser to interpret the file as compressed.  The second line tells the browser the content type of the decompressed document, which in this case is text/javascript.

The other two headers are not for the faint of heart.  They will cause the client browser to cache the file for 24 hours.  This is risky because it tells the browser not to bother checking to see if the file has changed, meaning that any changes you make to the JS file will not propagate to any clients that already have it cached.   (You can always force them to re-check the file by renaming the .php file, and then referencing that new name in the HTML header of your pages).  You can omit the cache-control and expires headers and the pre-compressed gzip will still load normally.

The include on the last line loads the compressed version of the .js file inline into the document.

You can then verify in Fiddler (or in your web server logs) that the compressed version of the Javascript is loaded via the .php file.

This technique should work with any static content that you want to pre-compress.  Of course, you can generalize the code and create a .php file that will take a query string parameter so that you can re-use the file for any of your pre-compressed content.

This example was tested with PHP/Apache on Linux using Chrome 19, Firefox 13, and (arg) even Internet Explorer 9.   It should work with PHP on any platform, and with any reasonably competent browser.

 

About Scott

I'm a computer guy with a new house and a love of DIY projects. I like ranting, and long drives on your lawn. I don't post everything I do, but when I do, I post it here. Maybe.
Bookmark the permalink.

Leave a Reply