10

Example:

RewriteCond %{REQUEST_URI}::$1 ^(.*?)/?(.*)::\2$

Looks like this operator is nowhere to find in any reference or manual. Where can I find it or anyone could explain what this operator does?

anubhava
  • 761,203
  • 64
  • 569
  • 643
Felix
  • 269
  • 1
  • 11
  • 1
    Can you send a full RewriteCond line that has a double colon? – Zeki Jun 03 '16 at 03:11
  • Example added. I kind of know what it does, but I'm looking for some deeper insight or official document if it's available since I can't find it when googling. – Felix Jun 03 '16 at 03:23

1 Answers1

12

Rules like this:

RewriteCond %{REQUEST_URI}::$1 ^(.*?/)(.*)::\2$
RewriteRule ^(.*)$ - [E=BASE:%1]

can also be written as (using ## as fixed delimiter on either side of condition):

RewriteCond %{REQUEST_URI}##$1 ^(.*?/)(.*)##\2$
RewriteRule ^(.*)$ - [E=BASE:%1]

Explanation:

  • You could use $1 captured from RewriteRule in your RewriteCond because mod_rewrite actually processes a ruleset backwards. It starts with the pattern in the RewriteRule, and if it matches, goes on to check the one or more RewriteCond.
  • So as you can see in a RewriteCond, the LHS (left-hand side / test string) can use backreference variables e.g. $1, $2 OR %1, %2 etc but RHS (right-hand side) i.e. condition string cannot use these $1, $2 OR %1, %2 variables.
  • Inside the RHS condition part only backreference we can use are internal back-references i.e. the groups we have captured in this condition itself. They are denoted by \1, \2 etc.
  • In your RewriteCond first captured group is (.*?/). It will be represented by internal back-reference \1.
  • As you can mark out that this rule is basically finding RewriteBase dynamically by comparing %{REQUEST_URI} and $1. An example of %{REQUEST_URI} will be /directory/foobar.php and example of $1 for same example URI will be foobar.php. ^(.*?/)::(.*)\1$ is putting the difference in 1st captured group %1 or \1. Here it will populate %1 or \1 with the value /directory/ which is used later in setting up env variable %{ENV:BASE} i.e. E=BASE:%1.
Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • The 5th bullet point puzzles me a little. How this work: ^(.*?/)::(.*)\1$ is putting the difference in 1st captured group %1 or \1? – Felix Jun 07 '16 at 00:57
  • From my understanding, :: operator is simply delimiter, How can a delimiter capture the difference between groups? – Felix Jun 07 '16 at 00:58
  • And for this one, (.*?/), what's the difference with (/), using /directory/foobar.php as an example. the 1st group captured would be both (/), but when I use ^(/)(.*)::\2$, it's not working – Felix Jun 07 '16 at 01:00
  • 1
    For the given examples `%{REQUEST_URI}::$1` expression makes it `/directory/foobar.php::foobar.php` on LHS. On RHS we have regex as`^(.*?/)(.*)::\2$` will place `/directory/` in `%1` and `.*` before `::` makes it capture `foobar.php` in `%2`. After `::` we have back-reference to `\2` which is `foobar.php`. [Also check this regex demo to understand it better](https://regex101.com/r/zM1aT5/1) – anubhava Jun 07 '16 at 03:38
  • 2
    Good explanation! Thanks. – Felix Jun 07 '16 at 03:47
  • I have one minor finding. RHS can also be written as ^(.*/)(.*)::\2$, which yields exact result but one char shorter. Is there any situation you can think of this one doesn't work? – Felix Jun 07 '16 at 03:58
  • Yes you're right `^(.*/)(.*)::\2$` can also be used. – anubhava Jun 07 '16 at 04:03
  • I still don't get it. How can `/directory/foobar.php` be `/directory/foobar.php::foobar.php` on LHS ? Logs show clearly:`RewriteCond: input='/directory/foobar.php::/directory/foobar.php' pattern='^(.*?/)(.*)::\\2$' => not-matched` so I'm still trying to figure out a case where this rule would actually match anything – fabmlk Mar 12 '20 at 10:12
  • If `%{REQUEST_URI}` and `$1` are both `/directory/foobar.php` then `%{ENV:BASE}` would be just `/`. If .htacces is in `/directory` then `%{ENV:BASE}` would be `/directory/`. – anubhava Mar 12 '20 at 10:28