now | writings | rss | github | twitter | contact

static files with apache 2 + mod_proxy + mod_rewrite + mongrel + rails

posted to writings on feb 22nd, 2009 with tags apache, nerd, openbsd, rails, and ruby

corduroy does everything over ssl and because of that, it was noticeably slow on my openbsd laptop with firefox 3. using tamper data, i was able to observe that firefox was not caching any of the stylesheets or javascript files, so on every page load it would have to re-fetch these few files (one of them being prototype at a hefty 28kb post-gzip). that ultimately seemed to be a quirk in firefox on openbsd since the same setup with the same version of firefox on my mac was caching them fine and safari was as well.

with the help of rails' stylesheet_link_tag et al. appending urls with "?1214348293", i was able to use apache's ExpiresActive to force very long expiration times on these static assets:

ExpiresActive On
<FilesMatch "\.(ico|js|css)$">
    ExpiresDefault "access plus 1 year"
    Header set Cache-Control public
</FilesMatch>

yet even with this enabled, tamper data was showing that the server-side headers were not reflecting the far-off expiration times i was expecting. i battled with it for a while until giving up since the problem only seemed to slow down firefox on openbsd.


i recently came across proctitle support for mongrel and after installing it on a few of my rails sites, the first thing that it showed me was that one of the mongrel processes for jcs.org was processing a large static file (which is a stupid animated gif i was using as a signature on a forum). i looked into it some more and couldn't figure out why mongrel was even seeing that request, since the apache mod_rewrite rules should have handled it inside apache directly. the request was not showing up in the rails application log, so i knew it was being handled by mongrel directly and being passed back up to apache.

i ran a tcpdump on lo0 and saw that indeed, apache was sending the request for the static file through to one of the mongrel proxies listening on a port on 127.0.0.1.

while mongrel was processing and returning on these static requests quickly, the mongrels needed to always be available to handle dynamic requests.

i was using the fairly standard apache configuration for rails with a mongrel cluster, which should only proxy requests through to a mongrel processes if the request is for a file that doesn't translate to an existing, static file:

ProxyPass / balancer://jcs/
ProxyPassReverse / balancer://jcs/

RewriteEngine On
...
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ balancer://jcs%{REQUEST_URI} [P,QSA,L]

i enabled debugging in mod_rewrite but it was telling me it was passing the request for the static file through to apache and not the mongrel proxy:

75.48.205.43 - - [22/Feb/2009:22:01:41 --0600] [jcs.org/sid#2052afef8][rid#20375e098/initial] (2) rewrite '/' -> 'balancer://jcs/'
...
75.48.205.43 - - [22/Feb/2009:22:01:54 --0600] [jcs.org/sid#2052afef8][rid#200c24098/initial] (1) pass through /tmp/Scorpion_escalator.gif

after some more debugging and reading, it suddenly became painfully obvious that using "ProxyPass /" changes mod_rewrite's concept of "pass" and so when mod_rewrite logs "pass through" it is passing it through to mod_proxy and not handling it internally. removing the ProxyPass line did indeed stop the request from getting passed through to the proxy and handled internally in apache.

with the ProxyPass line removed from all of the configurations, i confirmed that static assets are finally being handled from apache itself, and now with the proper expiry times that ExpiresActive is able to set. firefox on openbsd is as speedy as everything else for my sites, which should be beneficial for all 1 of my customers (me) using openbsd. i suppose if mongrel had been slower at serving static files i would have noticed this issue sooner, but i can't really complain about its performance.

Comments? Contact me via Twitter or e-mail.