1

Is it possible to apply a .htaccess rewrite-rule on an already rewritten URL?

Example: I want to resize my pictures with timthumb.php (most recent version, so there should be no security flaw any more). But the URL should look fine, therefore I created this rewrite-rule:

RewriteRule ^rz/(.*)x(.*)/r/(.*) /themes/Nerdtalk/timthumb.php?src=$3&h=$2&w=$1&q=80

This rule is working fine, but before starting timthumb.php which may re-direct to a cached file, I want Apache to check, if the file exists and let Apache redirect to the cached file, so timthumb.php won't be started. Therefore I created this ruleset:

RewriteCond %{SCRIPT_FILENAME} timthumb.php
RewriteCond %{QUERY_STRING} src=(.*)\.(png|jpe?g)&h=([0-9]+)&w=([0-9]+)&q=([0-9]+)
RewriteCond %{DOCUMENT_ROOT}/cache/%1-%4-%3-1-%5.%2 -f
RewriteRule ^.* /cache/%1-%4-%3-1-%5.%2 [L]
  1. Can an already rewritten URL be rewritten a second time?

  2. Is the second part correct?

  3. If there is a possibility to merge these two rulesets, can you please tell me how?

LazyOne
  • 158,824
  • 45
  • 388
  • 391
Phil
  • 13
  • 4

2 Answers2

2

Can an already rewritten URL be rewritten a second time?

Yes. When rewrite occurs (rule with the [L] flag or end of htaccess), mod_rewrite goes to next iteration and starts matching all rules again from start. That's why you need to build your rules with rewrite loop in mind (especially if you use this sort of pattern ^(.*)$ for matching).

If you write a rule without [L] flag, then rewrite continues on the same iteration, so any rules placed below current may rewrite URL again to a completely different.

Is the second part correct?

I would say Yes (although not checking on the actual Apache). I'm just not sure about first rewrite condition (just never used %{SCRIPT_FILENAME} myself so unsure how exactly it works) -- I would use %{REQUEST_URI} timthumb\.php$ or something like that.

The only thing that I would add is ? at rewrite target to get rid of newly created (1 rule above) query string: RewriteRule ^.* /cache/%1-%4-%3-1-%5.%2? [L]. But it should work fine as is now.

If there is a possibility to merge these two rulesets, can you please tell me how?

Place them one after another in the same order you have here. You cannot make it a single rule as 2nd rule will only work if thumbnail is already cached.

If it will not work straight away (as Apache's variables (%{SCRIPT_FILENAME}, %{QUERY_STRING} etc) may not have proper values straight away) try adding [L] flag to first rewrite rule -- it will force next iteration which populates those variables with proper values for sure, and, depending on the rest of your rules that you may have before this one, it will reach 2nd ruleset where 2nd rewrite will occur.

If you want -- you can rewrite it completely to check and serve cached file straight away (if present) and then (if still nothing) rewrite image to be processed by timthumb.php, but that will do exactly the same job as aforementioned rules, just a bit differently.

LazyOne
  • 158,824
  • 45
  • 388
  • 391
  • Thanks for your detailed explanation. But isn't it faster to let Apache process the check of file-existence instead of starting a PHP-script every time? – Phil Aug 23 '11 at 12:21
  • @Phil What are you talking about? I think you are confused how all this works. Please describe your understanding of the above mentioned rules. – LazyOne Aug 23 '11 at 12:27
  • The first rule rewrites a URL like /rz/714x357/r/screen.jpg to /themes/Nerdtalk/timthumb.php?src=screen.jpg&h=357&w=714&q=80. – Phil Aug 23 '11 at 12:40
  • Second rule checks: - if the requested scriptname equals timthumb.php - if the query string of requested URL matches src=(.*)\.(png|jpe?g)&h=([0-9]+)&w=([0-9]+)&q=([0-9]+) - if a file exists on DOCUMENT_ROOT/cache/%1-%4-%3-1-%5.%2 (for better unterstanding: DOCUMENT_ROOT/cache/screen-714-357-1-80.jpg) And if all these Conditions are true, then the URL gets rewritten and the rewriting will aborted then ([L]). The reason for the whole thing is, that in my understanding Apache should do the work of checking, whether a cached file exists or not, much faster than a PHP-script. – Phil Aug 23 '11 at 12:40
  • Yes -- you are correct & that is exactly how it will work with these 2 rules (executed 1 after another): Apache-invoked check is faster than PHP as you have no PHP overhead (PHP needs to be initialised, parse PHP script (if no code-caching involved) and then execute it. It can make some difference on very busy / shared hosting servers. – LazyOne Aug 23 '11 at 12:55
  • One thing that you still have to keep in mind: `[L]` does not necessary mean _"rewriting will aborted"_ -- instead think that `[L]` means end of iteration and that there always be another iteration after this (useful link: http://stackoverflow.com/questions/6797998/rewriterule-last-l-flag-not-working ). If you build your rules properly then no need to worry about other iterations/rewrite loop. These rules alone will not be caught in rewrite loop. – LazyOne Aug 23 '11 at 12:57
  • And I am on a shared host - I'm glad, that my understanding of the code was correct. I played around a bit and tried some (hopefully) optimized RegExp in the conditions, additionally I moved the check, if a cached file exists before the first rewrite. I will post this (working) ruleset later this day. – Phil Aug 23 '11 at 13:04
  • @Phil There is no REAL need to "move the check, if a cached file exists before the first rewrite" -- the difference will be invisible for you, even under heavy load (well .. 0.00001s diff or smaller), unless you reduce number of matches/regex complexity (even then the difference will be extremely small under the load -- it's only single rule, not 30-40 of them). – LazyOne Aug 23 '11 at 13:13
0

Can an already rewritten URL be rewritten a second time?

Yes, it can. It's default behaviour and if you would want to change it, you could use flags like [L]: http://httpd.apache.org/docs/current/rewrite/flags.html

Is the second part correct?

I'm not sure if you can use %1 etc. in RewriteCond directive

If there is a possibility to merge these two rulesets, can you please tell me how?

I would put cache in the same folder as your original url: ^rz/(.)x(.)/r/ If it exists - just serve it, if not - use php

Jacek Kaniuk
  • 5,229
  • 26
  • 28