GZip files with .htaccess and PHP 

 October 5, 2019

By  Jack Fisher

Many hosts have a set bandwidth clients can use. In this day and age, files are getting larger and heavier, but bandwidth costs aren’t getting much cheaper. So, one of the best and easiest things to do is to GZip.

CSS files for larger sites can become pretty large themselves. Gzipping or compressing these files has shown to provide a reduction in the neighborhood of 70-80% of the original file size, a fairly significant ‘weight loss’.

So obviously, GZipping CSS is great. But what about JS? JavaScript files are becoming increasingly huge, so what should we do about that?

The article from Fiftyfoureleven that is linked to suggests using the following PHP snippet:

<?php
    ob_start ("ob_gzhandler");
    header("Content-type: text/css; charset: UTF-8");
    header("Cache-Control: must-revalidate");
    $offset = 60 * 60 ;
    $ExpStr = "Expires: " .
    gmdate("D, d M Y H:i:s",
    time() + $offset) . " GMT";
    header($ExpStr);
?>

and then the following .htaccess snippet:

AddHandler application/x-httpd-php .css
php_value auto_prepend_file gzip-css.php
php_flag zlib.output_compression On

So then the obvious solution for JS files would be to make a file called gzip-js.php, with the same PHP snippet with the content type modified to text/javascript, like the one seen at Perishable Press.

Then we run into a problem. For systems like WordPress or ExpressionEngine or Habari, where css or js files might be sent from many different folders, manually putting the .htaccess and gzip file may not be convenient, so I created this system.

gzip.php:

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

.htaccess:

################ Expires Control ################
ExpiresActive On
ExpiresDefault A0
<FilesMatch "\.(gif|jpg|jpeg|png|swf)$">
# 2 weeks
ExpiresDefault A1209600
Header append Cache-Control "public"
</FilesMatch>
<FilesMatch "\.(xml|txt|html)$">
# 2 hours
ExpiresDefault A7200
Header append Cache-Control "proxy-revalidate"
</FilesMatch>
<FilesMatch "\.(js|css)$">
# 3 days
ExpiresDefault A259200
Header append Cache-Control "proxy-revalidate"
</FilesMatch>

################## GZip Files ###################
<FilesMatch "\.js$">
AddHandler application/x-httpd-php .js
php_value default_mimetype "text/javascript"
</FilesMatch>
<FilesMatch "\.css$">
AddHandler application/x-httpd-php .css
php_value default_mimetype "text/css"
</FilesMatch>
<FilesMatch "\.(htm|html|shtml)$">
AddHandler application/x-httpd-php .html
php_value default_mimetype "text/html"
</FilesMatch>
php_value auto_prepend_file /absolute/path/to/gzip.php

This snippet allows me to control the expires and content type using htaccess instead of PHP, like in the other examples. And because I use the absolute path to gzip.php, I can ensure that GZip gets applied to all php, js, css, html, shtml, and htm files.

Jack Fisher


Hey - I'm Jack. I've been goofing around with code since 2002, and I've come a long way from my first Geocities site. I started Lateral Code 12 years ago and have been blogging ever since.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

Subscribe to our newsletter now!