3

I have a virtual host with multiple ServerAlias host1.example.com host02.example.com etc.

I match the same pattern and based on ServerAlias I want to proxy them to different backend hosts. What I have is :

Snippet from config:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www.)?host1.example.com [NC]
RedirectMatch  ^/~([^/]*)$ http://host01.example.com/~$1/';
RewriteRule    ^/~([^/]*)/$ %{HTTP:X-Scheme}://users-host01.example.com/$1/   [P]
RewriteRule    ^/~([^/]*)/(.*)$ %{HTTP:X-Scheme}://users-host01.example.com/$1/$2 [P]

RewriteCond %{HTTP_HOST} ^(www.)?host02.example.com [NC]
RedirectMatch  ^/~([^/]*)$ http://host02.example.com/~$1/';
RewriteRule    ^/~([^/]*)/$ %{HTTP:X-Scheme}://users-host02.example.com/$1/   [P]
RewriteRule    ^/~([^/]*)/(.*)$ %{HTTP:X-Scheme}://users-host02.example.com/$1/$2 [P]

This works but only for the first defined ruleset. How can I make them both work at the same time?

2

A RewriteCond only applies to the very next RewriteRule. It never applies to a RedirectMatch. Those are handled by mod_alias not by mod_rewrite.

To get this to work you will need to repeat your RewriteCond for each rule and use a RewriteRule in place of RedirectMatch.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www.)?host1.example.com [NC]
RewriteRule    ^/~([^/]*)/$ %{HTTP:X-Scheme}://users-host01.example.com/$1/   [P]
RewriteCond %{HTTP_HOST} ^(www.)?host1.example.com [NC]
RewriteRule    ^/~([^/]*)/(.*)$ %{HTTP:X-Scheme}://users-host01.example.com/$1/$2 [P]

RewriteCond %{HTTP_HOST} ^(www.)?host02.example.com [NC]
RewriteRule    ^/~([^/]*)/$ %{HTTP:X-Scheme}://users-host02.example.com/$1/   [P]
RewriteCond %{HTTP_HOST} ^(www.)?host02.example.com [NC]
RewriteRule    ^/~([^/]*)/(.*)$ %{HTTP:X-Scheme}://users-host02.example.com/$1/$2 [P]

I'm not sure exactly what your RedirectMatch is actually supposed to be doing, so I just removed it for now.

If you are using Apache 2.4 or later, you could wrap your RedirectMatch in an if block:

<If "req('Host') =~ /^(www.)?host1.example.com/">
    RedirectMatch  ^/~([^/]*)$ http://host01.example.com/~$1/';
</If>

I'm not sure if RewriteRules are compatible with <If> or not. You could try putting all of your rules inside those <If> blocks and forgoing the RewriteCond altogether.

  • It looks like the intention of RedirectMatch was to append a trailing slash (when there is just one path segment), however, there is an erroneous '; on the original directive - so this may not be being used anyway? (And it should be a RewriteRule to ensure it executes early and can probably apply to all hostnames, so may not need a condition, depending on the ServerAlias config.) – MrWhite Jan 10 at 16:35
  • @MrWhite any comments on my edit about the compatibility of rewrite and If? – Stephen Ostermiller Jan 10 at 16:36
  • Your edit looks OK to me... it will restrict the RedirectMatch directive to requests for that Host only. The other mod_rewrite directives will be processed as normal (first). However, I think putting all the directives in the <If> - as you suggest - would be preferable. And change the RedirectMatch to RewriteRule in the process - this isn't a serious issue, as there is no conflict, but it is "confusing" as the directives are not processed in the order they appear in the file. – MrWhite Jan 11 at 0:05
  • 1
    AFAIK, the only issue with mod_rewrite and <If> containers is if you have mod_rewrite directives both inside and outside the <If> container and are expecting them all to be processed. If the mod_rewrite directives inside the <If> container are processed (because the if-expression is met) then these will completely overide the directives outside of the <If> container (by default)! In much the same way mod_rewrite directives in child .htaccess files completely override any parent .htaccess file (or <Directory> container). You can use mod_rewrite inheritance to overcome this. – MrWhite Jan 11 at 0:18
  • However, these directives look as if they could be simplified - there may not be any need to duplicate these directives for each host if the naming of the hostnames is consistent. (I've added an answer/addendum.) – MrWhite Jan 11 at 0:47
2

This is really just an addendum to @Stephen's answer (which already appears to address the main problem).

However, your directives could possibly be greatly simplified. If you are using the "same patterns" for each rule block and your hostnames are consistent, ie. host01.example.com maps to users-host01.example.com etc. (Although you have a slight inconsistency in your first example: host1 to users-host01?) Then there is no need to repeat your directives for each hostname.

Also, if you only have non-www hostnames in your ServerAlias then there is no need to check for the www subdomain in your directives. (But this should probably be canonicalised earlier if you are allowing this.)

So, your directives could potentially be simplified to:

RewriteEngine On
RewriteRule ^/~([^/]*)$ http://%{HTTP_HOST}/~$1/ [R=302,L]
RewriteRule ^/~([^/]*)/(.*)$ %{HTTP:X-Scheme}://users-%{HTTP_HOST}/$1/$2 [P]

Your first RewriteRule is superfluous as it is handled by the second (if there is nothing after the trailing slash then the $2 backreference is simply empty).

As noted in comments above, your RedirectMatch directive had an erroneous '; on the end of the target URL. This was also a temporary 302 redirect and should probably be a 301 (permanent). But only change this to a 301 once you have confirmed it works as expected.

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

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