Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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?

share|improve this question

migrated from stackoverflow.com Apr 24 '13 at 0:29

This question came from our site for professional and enthusiast programmers.

    
What about MultiViews? –  Felipe Alameda A Apr 12 '13 at 19:00
    
MultiViews are enabled. Could that be the cause? How so? –  deceze Apr 13 '13 at 0:26
    
Interesting. I'll check if that makes a difference. –  deceze Apr 13 '13 at 5:37
    
Nope, sorry to report that it didn't change a thing. –  deceze Apr 15 '13 at 0:41
1  
@Servant What isn't clear, please? The RewriteCond -f rule doesn't see the rewritten file, even though the RewriteRule does. The question is why? –  deceze Apr 15 '13 at 23:13

3 Answers 3

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

share|improve this answer
1  
RewriteBase is for specifying a URL, this doesn't really apply in this case. Using the absolute path in the RewriteCond does work, but to avoid having system-specific .htaccess files I'd like to avoid that. I'm also simply interested in an explanation for why it doesn't work, since I don't really need the RewriteCond at all. –  deceze Apr 19 '13 at 1:17

If it works on localhost, but not test.com, I would guess its a problem with your filesystem or it is related to more general site configuration that you may be taking for granted. Did you install both operating systems, and then install the same packages, using the same procedure on both machines? What did each of the default configurations look like that shipped with each Apache? What about packages that modified default configuration before you even got a look?

Maybe your apache versions are near similar, but if they were from different sources, default configuration may have been different and may have been subsequently modified differently. Verify your configuration starting from the top. What is different? Trying making them look exactly the same (not just your rewrites, but Virtual-Host or Server contexts.)

The context of your Rewrite directives would likely help (as I see this being an issue of scope). Are the rewrites in the VirtualHost, Document-Root, Directory context?

The biggest difference I see from your posts between success and failure is this:

Relative

RewriteCond img/$2/$3/$4/$1 -f

Absolute

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

Apache is capable of finding the file, so it shouldn't be permissions or any such. Are you using any Alias directives (localhost vs test.com)? If not, maybe you should try to Alias this specifically:

/var/www/myapp/current -> /var/www/myapp/releases/20130418090750

Put at least one hard path in your root configuration that doesn't require a symlink. You can wrap up your rewrites in a Directory context...

<Directory /var/www/myapp/current/webroot>
    rewrite statements...
</Directory>

What happens if you maintain your broken configuration on test.com, but where you currently have symlinks, try copying actual directories (temporarily) starting at the root. Just verify there is not some loose Symlink setting floating. Get rid of as many symlinks as possible and put them back in 1 by 1.

share|improve this answer

The backreferences are "up" (rewriteCond) to "down" (RewriteRule). In the example you publish is like "down" to "up". And, the backreferences are with % not with $.

RewriteCond backreferences: These are backreferences of the form %N (0 <= N <= 9). %1 to %9 provide access to the grouped parts (again, in parentheses) of the pattern, from the last matched RewriteCond in the current set of conditions. %0 provides access to the whole string matched by that pattern.

You could try this:

RewriteBase /
RewriteCond %{REQUEST_URI} /img/([a-z0-9]{4})([a-z0-9]{4})([a-z0-9]{4})([a-z0-9]{28}\.\w+)$
RewriteCond %{DOCUMENT_ROOT}/test/%1/%2/%3/%4 -f
RewriteRule .* img/%1/%2/%3/%4 [L]

First the regular expression to find the variables. The path to the file containing the variables is constructed and verified if the file exists

I use the %{DOCUMENT_ROOT} for test on my local server. If you don't has this image files in the same DocumentRoot you need the FULL PATH like:

RewriteCond /var/www/cache_images/test/%1/%2/%3/%4 -f
share|improve this answer
    
Rewrites are evaluated in the order RewriteRule match, RewriteCond match, RewriteRule rewrite. So "down to up" works. RewriteRule back references are referred to by $n and RewriteCond back references are referred to by %n. It actually works. –  deceze Feb 1 at 19:56
    
You're right. You could edit your question to place the final version of your settings, so the solution would be clear. I'll edit my answer to clear the error (up and down), and in any case, my example also works :) –  Gabriel Pérez S. Feb 1 at 20:12

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.