24 November 2008

Your Apache May Be Cheating You

In a fit of premature optimisation I figured the java/jetty/mysql part of iconfu should not be serving images, even if the database is the primary point of reference for image content, and have apache serve these images instead. Here's the plan: the server gets a request for an icon. The corresponding file doesn't exist, so the java application creates the file and also serves the content. On subsequent requests for the same file, apache should serve the file directly without invoking the java application. Here's the VirtualHost configuration I ended up with:

NameVirtualHost *:80

<VirtualHost *:80>
        ServerName example.com
        ServerAlias www.example.com
        DocumentRoot /apps/example.com/
        RewriteEngine On

        RewriteCond "%{DOCUMENT_ROOT}%{REQUEST_FILENAME}" !-f
        RewriteRule "^/(.*)$" "http://127.0.0.1:8000/$1" [P,QSA,L]

        ProxyPassReverse / http://127.0.0.1:8000/
        ProxyPreserveHost on

        <Location />
          Order allow,deny
          Allow from all
        </Location>

        <Location /WEB-INF >
          Order allow,deny
          Deny from all
        </Location>

        ErrorLog /var/log/apache2/example.com.error.log
</VirtualHost>

You will notice that in this configuration, you don't need to specify all the static stuff you want apache to serve - star dot ping, star dot js, star dot css, star dot etc etc. The rule is quite simple: if the file exists, serve it, otherwise assume it's a request for a dynamic resource. The RewriteCond line does that for you. "!-f" is mod-rewrite-speak for "test that the file does not exist"

It took two days of twiddling and tweaking to get here, which was all the more shameful because I had thought this static-serving-apache stuff was all already working. I was so wrong. In particular, I had included the ProxyPass / http://127.0.0.1:8000/ directive, which apparently overrides the RewriteRule directive, causing everything to go through the proxy, regardless of whatever rewrite rules you have configured. I made this error as a result of reading other peoples' blogs, something you should never try at home. If I had written this entry before reading all those others, I would have been spared all the hassle. Fortunately however, now you are.

Another error I had made: the accursed trailing slash. Note carefully the difference between these two:

        DocumentRoot /apps/example.com/
        RewriteCond "%{DOCUMENT_ROOT}%{REQUEST_FILENAME}" !-f

and

        DocumentRoot /apps/example.com
        RewriteCond "%{DOCUMENT_ROOT}/%{REQUEST_FILENAME}" !-f

Both of these work, but the following doesn't:

        DocumentRoot /apps/example.com/
        RewriteCond "%{DOCUMENT_ROOT}/%{REQUEST_FILENAME}" !-f

In this last case, apache will always believe that the requested file does not exist, and will always forward to the proxy. And that sucketh, as Shakespeare might have said.

Here's an example of a blog entry you must never take rewriterule advice from (although the load-balancing suggestions appear to be accurate) - http://www.conandalton.net/2008/08/apache-2-modproxy-load-balancer.html. On the other hand, http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html is the fountain of apache-fu, read that before reading anything else.

No comments:

Post a Comment