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> | |