RewriteCond file-exists check fails for existing files

apache-2.2mod-rewrite

I have a weird scenario with this rewrite rule:

RewriteCond img/$2/$3/$4/$1 -f
RewriteRule ^img/(([a-z0-9]{4})([a-z0-9]{4})([a-z0-9]{4})[a-z0-9]{28}\.\w+)$ img/$2/$3/$4/$1 [L]

The directory structure for this is (simplified):

var/
  images/
    ..
  www/
    .htaccess
    img -> /var/images

In other words, images are stored outside the webroot in a general data-storage area and are linked into the webroot's img directory via a symbolic link. Image names are hashes. For efficiency, they're stored in a three-level deep directory hierarchy. For example:

0a808e34edaaeeffd973e4138789a4957d6b6a26.jpg

is stored at

images/0a80/8e34/edaa/0a808e34edaaeeffd973e4138789a4957d6b6a26.jpg

The rewrite rule simply rewrites the file name into the nested directory structure.

Now, the weird thing is that the RewriteCond works fine on my local systems but fails on a test server. The Apache debug log explicitly says that the "-f pattern doesn't match" for this condition. If I simply remove this condition the rewrite rule works fine and the image is served.

What could cause -f to fail for files which clearly exist?

FollowSymLinks is allowed. Apache version 2.2.22-6ubuntu2 installed via apt and hardly modified. Works on a local install of Apache 2.2.23 (via homebrew). Couldn't see any significant changes between the two versions that should cause this.


Some more possibly significant details about the actual directory structure:

$ ls -l /var/www/myapp/current
[snip] /var/www/myapp/current -> /var/www/myapp/releases/20130418090750

$ ls -la /var/www/myapp/current/webroot
[snip]
[snip] .htaccess
[snip] img -> /usr/local/var/myapp/images

The Apache webroot is configured as:

DocumentRoot /var/www/myapp/current/webroot

If I write the RewriteCond as:

RewriteCond /usr/local/var/myapp/images/$2/$3/$4/$1 -f

it works. I would prefer not to hardcode absolute paths though if at all avoidable.

May Apache be confused by the several levels of symlinks?

Best Answer

Actually, I've never seen yet an article that have a tutorial about RewriteCond to get a variable from the RewriteRule below it.. However, you can still try to add a RewriteBase directive on the top of your .htaccess file:

RewriteBase /www/

Or this, just give both a try:

RewriteBase /var/www/

Or you can try one of these sets of directives below but EXCLUDING any RewriteBase directive that listed above:

RewriteCond /www/img/$2/$3/$4/$1 -f
RewriteRule ^www/img/(([a-z0-9]{4})([a-z0-9]{4})([a-z0-9]{4})[a-z0-9]{28}\.\w+)$ /www/img/$2/$3/$4/$1 [L]

Or you can even give this a try:

RewriteCond /var/www/img/$2/$3/$4/$1 -f
RewriteRule ^var/www/img/(([a-z0-9]{4})([a-z0-9]{4})([a-z0-9]{4})[a-z0-9]{28}\.\w+)$ /var/www/img/$2/$3/$4/$1 [L]

I'm sorry for copying the format of your rewrite condition and rule, because you said that “it works on your local development system.” But this is the simply rule that I recommend to you if you just want to rewrite /$var1.$var2 into /images/0a80/8e34/edaa/$var1.$var2, you can even add the others statically, one by one:

RewriteRule ^([a-zA-Z0-9_-]+).([a-zA-Z]{3})$ /images/0a80/8e34/edaa/$var1.$var2

Or you can even try this if you want to rewrite /img/0a808e34edaaeeffd973e4138789a4957d6b6a26.jpg dynamically into /img/0a80/8e34/edaa/0a808e34edaaeeffd973e4138789a4957d6b6a26.jpg with condition if the dynamic URL where it remapping is a file:

RewriteCond /img/$2/$3/$4/$1\.$5 -f
RewriteRule ^img/(([a-z0-9]{4})([a-z0-9]{4})([a-z0-9]{4})[a-z0-9]{28})\.([a-z]{3})$ /img/$2/$3/$4/$1.$5

Remember that the characters on "0a80", "8e34" and "edaa" must be exact as 4 and the chars. in "eeffd973e4138789a4957d6b6a26" must be as 28 and in "jpg" must be as 3. I hope that this will work, but I doubt..

Related Topic