Conditional gzipping with Apache[source]
<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>
<glacius:macro name="legacy blargh banner">
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.
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
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.
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.
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>:
<glacius:code lang="apache"><![CDATA[SetOutputFilter DEFLATE]]></glacius:code>
I realized that that would make <strong>everything</strong> gzipped! But then I noticed you could
filter the... er... filter by mimetype, like so:
<glacius:code lang="apache"><![CDATA[# only text/html will be gzipped
AddOutputFilterByType DEFLATE text/html]]></glacius:code>
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>Here were my requirements:</p>
<li>Only one physical copy of the document exists on the server</li>
<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>
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
<code>.js</code> at the end. Simple enough, right? Sure.
Hopefully, you've deduced that <code>/gz</code> indicates that we want gzip compression; if it's
whether the <code>/gz</code> is tacked on the end or not.
Now we need to do the deflate stuff. How we do it? Well, the most obvious solution is something like this:
<glacius:code lang="apache"><![CDATA[<Directory /path/to/acronymulates>
want to compress. See?
<img glacius:src="dynamic_gzipping_acronymulates.png" alt="gzipping" />
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><strong>Location to the rescue!</strong></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:
<glacius:code lang="apache"><![CDATA[<LocationMatch /[1-9]\d*/gz>
And there's your dynamic gzipping using Apache. Note that these are real life examples.
Let's prove it with Firebug's help.
Without compression (<a href="http://acronymulator.com/1">http://acronymulator.com/1</a>):
<img glacius:src="dynamic_gzipping_11.png" alt="dynamic gzipping" />
With compression (<a href="http://acronymulator.com/1/gz">http://acronymulator.com/1/gz</a>):
<img glacius:src="dynamic_gzipping_1_gz1.png" alt="Dynamic gzipping: with compression" />
And here's proof that both URLs serve the same content:
<img glacius:src="dynamic_gzipping_md5sum.png" alt="MD5 sum comparison" />