Conditional gzipping with Apache[source]

xml
<glacius:metadata>
    <title>Conditional gzipping with Apache</title>
    <description>Old blog post about conditionally gzipping a file using Apache HTTP server</description>
    <category>Legacy blog posts</category>
    <category>Programming</category>
    <category>Apache</category>
    <category>Web servers</category>
</glacius:metadata>
<glacius:macro name="legacy blargh banner">
    <properties>
        <originalUrl>https://tmont.com/blargh/2009/8/conditional-gzipping-with-apache</originalUrl>
        <originalDate>2009-08-30T21:41:53.000Z</originalDate>
        <message>
            This has been slightly modified from the original to be more informative
            and less idiosyncratic. Also note that the two utilities I built are no
            longer around so these links are broken forever.
        </message>
    </properties>
</glacius:macro>
<p>
  I've never really experimented with some of the more low-level website optimizations (server side stuff, 
  like gzipping, caching and the like), mostly because I've never really had to. I'm not a server admin,
  and my sites don't generate enough traffic for me to really care about such minor issues. But then I 
  released two <a href="http://acronymulator.com/">little</a> <a href="http://linkurious.com/">utilities</a> wherein 
  users could just include a piece of JavaScript from my own server. You know, they would so something like this:
</p>
<glacius:code lang="html"><![CDATA[<script type="text/javascript" src="http://linkurious.com/js"></script>]]></glacius:code>
<p>
  where the <code>src</code> attribute was pointing to an external server; namely, <strong>my</strong> server. 
  If enough people start using these utilities (extremely unlikely, although they are pretty awesome) it could 
  be a strain on my server.
</p>
<p>
  So I decided to offer a plain text version and a gzipped version. But I didn't want to store two versions 
  of the scripts in separate places on my server just to satisfy that need. The reasons had nothing to do with 
  disk space or anything tangible; I just didn't "feel right" doing something so hackish.
</p>
<p>
  Anyway, once I figured out how to use <a href="http://httpd.apache.org/docs/2.2/mod/mod_deflate.html"><tt>mod_deflate</tt></a>:
</p>
<glacius:code lang="apache"><![CDATA[SetOutputFilter DEFLATE]]></glacius:code>
<p>
  I realized that that would make <strong>everything</strong> gzipped! But then I noticed you could 
  filter the... er... filter by mimetype, like so:
</p>
<glacius:code lang="apache"><![CDATA[# only text/html will be gzipped
AddOutputFilterByType DEFLATE text/html]]></glacius:code>
<p>
  But that still didn't help me. I needed <em>conditional</em> gzipping, like if the query string 
  said <code>?gz</code> it would know to serve the document gzipped; otherwise, it would just serve it 
  with no compression. But how to accomplish this? Apache's docs were no help. Luckily I'm fairly 
  proficient at <a href="http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html"><code>mod_rewrite</code></a>, and 
  I'm decently intelligent. Let's see if I can figure it out.
</p>
<p>Here were my requirements:</p>
<ol>
  <li>A request to <code>http://acronymulator.com/1</code> would serve the JavaScript document with no compression</li>
  <li>A request to <code>http://acronymulator.com/1/gz</code> would serve the JavaScript document with gzip compression</li>
  <li>Only one physical copy of the document exists on the server</li>
</ol>
<p>Let's get to work!</p>
<p>First, let's do the rewrite rules:</p>
<glacius:code lang="apache"><![CDATA[RewriteEngine On
RewriteRule ^/([1-9]\d*)(?:/gz)? /acronymulates/$1.js]]></glacius:code>
<p>
  The first line turns on the rewrite engine. The second line matches anything from the root 
  that is made up of numbers, at least one number long where the first number is not a zero, 
  with an optional <code>/gz</code> on the end. The rule finishes by internally redirecting those to 
  the acronymulates directory, and serves up the JavaScript file whose name is the number with 
  <code>.js</code> at the end. Simple enough, right? Sure.
</p>
<p>
  Hopefully, you've deduced that <code>/gz</code> indicates that we want gzip compression; if it's 
  omitted, we don't want gzip compression. Notice that it's redirecting to the real JavaScript file
  whether the <code>/gz</code> is tacked on the end or not.
</p>
<p>
  Now we need to do the deflate stuff. How we do it? Well, the most obvious solution is something like this:
</p>
<glacius:code lang="apache"><![CDATA[<Directory /path/to/acronymulates>
    SetOutputFilter DEFLATE
</Directory>]]></glacius:code>
<pre class="sunlight-highlight-httpd"></pre>
<p>
  This makes sense because the only thing in the acronymulates directory is the JavaScript files that we 
  want to compress. See?
</p>
<p class="text-center">
  <img glacius:src="dynamic_gzipping_acronymulates.png" alt="gzipping" />
</p>
<p>
  However, this will force gzip compression no matter what, and we want it be 
  <strong>conditional</strong>: only when the <code>/gz</code> is part of the URL. 
  What do we do?
</p>
<p><strong>Location to the rescue!</strong></p>
<p>
  The <a href="http://httpd.apache.org/docs/2.2/mod/core.html#location">Location</a> directive accomplishes
  this for us. more specifically, the <code>LocationMatch</code> directive accomplishes this for us. The 
  difference between Directory and Location is that Directory matches a physical directory on the filesystem, 
  whereas Location just matches a virtual directory (i.e. the path of the URL: <code>/1/gz</code>, in our case). 
  Now it's fairly obvious what the solution should be:
</p>
<glacius:code lang="apache"><![CDATA[<LocationMatch /[1-9]\d*/gz>
    SetOutputFilter DEFLATE
</LocationMatch>]]></glacius:code>
<p>
  And there's your dynamic gzipping using Apache. Note that these are real life examples.
  Let's prove it with Firebug's help.
</p>
<p>
  Without compression (<a href="http://acronymulator.com/1">http://acronymulator.com/1</a>):
</p>
<p class="text-center">
  <img glacius:src="dynamic_gzipping_11.png" alt="dynamic gzipping" />
</p>
<p>
  With compression (<a href="http://acronymulator.com/1/gz">http://acronymulator.com/1/gz</a>):
</p>
<p class="text-center">
  <img glacius:src="dynamic_gzipping_1_gz1.png" alt="Dynamic gzipping: with compression" />
</p>
<p>
  And here's proof that both URLs serve the same content:
</p>
<p class="text-center">
  <img glacius:src="dynamic_gzipping_md5sum.png" alt="MD5 sum comparison" />
</p>