2

With my limited regular expression and mod_rewrite abilities, I'm attempting to rewrite certain image requests so I can alter the output with a php script. Here's what I have:

RewriteRule ^(public|uploads)/([A-Za-z0-9/_-]+).(JPEG|JPG|GIF|PNG|jpeg|jpg|gif|png)$ public/images.php?%{QUERY_STRING}&src=$1/$2.$3 [L]
#            [       1      ] [       2       ] [                  3              ]

This does work, but it's too greedy and doesn't require the query string, which is important - otherwise all images requests would be rewritten. I tried putting a ? or ?(.*) in the rule, and I would either get an internal server error or it didn't seem to solve the problem (most likely because I didn't do it correctly). I also tried %{QUERY_STRING} at the end of the condition, but that didn't seem to affect anything.

Here's what I want to happen:

  • Any requests for public/ or uploads/...
  • Followed by any path to an image (file extension case insensitive)...
  • Followed by a query string...
  • ...should rewrite to public/images.php with the original query string, and add one aditional parameter: src, which contains the actual path to the image (the rewritten part).
  • Extra "would-be-nice", but not necessary: Restrict the rule to only rewrite the url if the query string contains at least one item from a set of parameters. For example, only if one of the width=, height= or contrast= params are present. If this makes things bloated or complicated, I'm not worried about it.

So for example, a request for:

uploads/images/my_folder/test.jpg?width=320&height=220

Should be served by:

public/images.php?width=320&height=220&src=public/images/my_folder/test.jpg

The .htaccess file is in my root directory, as well as the public and uploads directories.

I want to avoid absolute urls, because I want this to be portable without needing to edit. I've done a good deal of googling and reading related SO posts, and still can't figure this one out. How can I patch this rule to do what I want, or is there a better way to do write this altogether?

Edit: Just want to note that this rule worked for me previously:

RewriteRule ^(public|uploads)/([A-Za-z0-9/_-]+).(JPEG|JPG|GIF|PNG|jpeg|jpg|gif|png)/([0-9]+)$ public/images.php?width=$4&src=$1/$2.$3

...but only for requests like uploads/my_folder/image.jpg/280 - I used the 280 as the width, but now I want to accept combinations of multiple parameters in no particular order:

Wesley Murch
  • 101,186
  • 37
  • 194
  • 228
  • You'll want to use `\.` instead of `.` before the file extension. – Tim Pietzcker Sep 01 '11 at 20:52
  • Tried replacing the `.` with `\.` in all instances, but it seemed nothing changed. The rule still works, but is too greedy. – Wesley Murch Sep 01 '11 at 20:54
  • Oh, I know, sorry. My comment has nothing to do with your problem (I don't know anything about rewrite rules, so I didn't even try to answer your question); I just noticed this one omission - that's why I put it into a comment, not an answer. – Tim Pietzcker Sep 01 '11 at 20:59

1 Answers1

2

Two approaches:

1. Add a condition to only rewrite when query string is not empty (can be anything):

RewriteCond %{QUERY_STRING} !^$
RewriteRule ^(public|uploads)/([A-Za-z0-9/_-]+)\.(jpe?g|gif|png)$ public/images.php?src=$1/$2.$3 [NC,QSA,L]

2. Add a conditions to only rewrite when query string is not empty and has at least one of those parameters present (but the value can be empty):

RewriteCond %{QUERY_STRING} (^|&)width=([^&]*)(&|$) [OR]
RewriteCond %{QUERY_STRING} (^|&)height=([^&]*)(&|$) [OR]
RewriteCond %{QUERY_STRING} (^|&)contrast=([^&]*)(&|$)
RewriteRule ^(public|uploads)/([A-Za-z0-9/_-]+)\.(jpe?g|gif|png)$ public/images.php?src=$1/$2.$3 [NC,QSA,L]

I have not really tested #2 .. but should work fine (copied from fully working code).


NOTES:

You can replace ^(public|uploads)/([A-Za-z0-9/_-]+)\.(jpe?g|gif|png)$ by ^((?:public|uploads)/(?:[A-Za-z0-9/_-]+)\.(?:jpe?g|gif|png))$ .. and then instead of src=$1/$2.$3 use just src=$1

Alternatively -- replace ^(public|uploads)/([A-Za-z0-9/_-]+)\.(jpe?g|gif|png)$ by ^(?:public|uploads)/[A-Za-z0-9/_-]+\.(?:jpe?g|gif|png)$ and then use src=${REQUEST_URI} -- the only difference that src= parameter will start with leading slash.

LazyOne
  • 158,824
  • 45
  • 388
  • 391
  • The first one still seems to rewrite even without the query string, but it looks like the second **and** third example are working. Good call on mapping the expression to a single variable, that makes sense. Thanks for tightening up the file extension bit as well. This is working for me, so case closed - but maybe remove the first example (unless I made a mistake and it really should only work if the QS is present). – Wesley Murch Sep 01 '11 at 21:31
  • No -- first one will work regardless of query string presence. The #3 -- my mistake (copy-paste) you need to remove either `%{QUERY_STRING}&` or `QSA,` as they both do the same job (although it does not affect the result URL in any way, just saves few CPU cycles). I prefer removing `%{QUERY_STRING}&` as it is easier to read and rule is a bit shorter. – LazyOne Sep 01 '11 at 21:41
  • Hmm, I tried again and the first one would always run regardless of QS or not, could be my server config or something (there are no conflicting rules). Thanks for the note it's working. Since this runs in the root dir and affects all requests, less processing time is better, hopefully I will be able to spend a few days studying mod_rewrite some time so I can understand this all to a greater degree. – Wesley Murch Sep 01 '11 at 21:44
  • Which #1 -- old one or new one? New #1 will only run IF there is query string (which can be anything, even `say=meow`) -- that's 100% tested. – LazyOne Sep 01 '11 at 21:54
  • Yeah, the old #1 ran even without the QS. The new #1 works great and so does #2. Thanks so much for putting in effort to provide the "bonus" part, this will help me to restrict the actual *values* of the params as well as the presence of them. – Wesley Murch Sep 01 '11 at 21:59