[Webinar] Streamline your web hosting managementRegister Today

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 798
  • Last Modified:

Apache 2.2 rewrite using rewritemap

Below I have two configurations for rewrite to do the same thing, each with a problem, your help much appreciated.

SETUP 1:
You will notice in this setup that the line with "tolower" is commented. For some reason I can't get "tolower" to work. The rest works as it should, that is the rewritemap does it's thing and redirects domains.

I have of course made the name of it different to the function it used just in case that was an issue but it made no difference. I.e.: "RewriteMap tolower int:tolower" tested as: "RewriteMap lcase int:tolower"

This is a temporary setup because it works (except for the lower case issue). Below however is what I want to end up with.

SETUP 2:
...is just moving the code in the .htaccess file to the httpd.conf file. This would be much better since that is far more efficient but it simply stops working. I thought that per-server rewrites were done before per-dir rewrites so to me it should do this just fine before even getting to the .htaccess files.

I will eventually make the map file a database lookup instead of a text file for efficiency but right now I'm just trying to get it working properly.

Last, does anyone know how to write this so that there is not two calls to the map?

As you see can right now it needs one for the RewriteCond and then another for the following Rule. Without the RewriteCond you get an infinite loop in the case of the URL not being in the map... which should be most of the time. I suspect that this is the way it needs to be so I'm not fussed if this is not answered (but it would be cool to find a better way.)

I haven't read anything where this would make a difference but just in case I will mention that this is vhost setup which I guess could mean that the code has to go in the vhost file instead of httpd.conf but like I said, I haven't found any documentation to suggest that... it's just conjecture on my part in lieu of actually knowing the solution.

-------------------------------------------------------------
SETUP 1
-------------------------------------------------------------
httpd.conf
RewriteEngine On

RewriteMap domains txt:/etc/apache2/domains-mapping.txt
RewriteMap tolower int:tolower

.htaccess
RewriteEngine On
RewriteCond %{HTTPS}s ^(on(s)|off)$
RewriteRule ^ - [E=s:%2]

#RewriteCond $(tolower:%{HTTP_HOST}} (.+)
RewriteCond %{HTTP_HOST} (.+)
RewriteCond ${domains:%1} !^$
RewriteRule (.*) http%{ENV:s}://${domains:%1}/$1 [NE,R,L,QSA]
-------------------------------------------------------------

-------------------------------------------------------------
SETUP 2
-------------------------------------------------------------
httpd.conf
RewriteEngine On

RewriteMap domains txt:/etc/apache2/domains-mapping.txt
RewriteMap tolower int:tolower

RewriteCond %{HTTPS}s ^(on(s)|off)$
RewriteRule ^ - [E=s:%2]

#RewriteCond $(tolower:%{HTTP_HOST}} (.+)
RewriteCond %{HTTP_HOST} (.+)
RewriteCond ${domains:%1} !^$
RewriteRule (.*) http%{ENV:s}://${domains:%1}/$1 [NE,R,L,QSA]
-------------------------------------------------------------

Open in new window

0
RegProctor
Asked:
RegProctor
  • 13
  • 8
3 Solutions
 
HackneyCabCommented:
First off, an easy one. You've accidentally used a parenthesis ( instead of a brace { in your rewritecond which uses tolower, which probably explains why tolower is not doing what you expect.

Second, to move RewriteRule directives into httpd.conf, you need to anchor the paths because whereas .htaccess files assume the current working directory, the server config httpd.conf cannot make that assumption (because it's not located in any public directory). So try adding an absolute path to your RewriteRule directives, like this:

RewriteRule /(.*) http%{ENV:s}://${domains:%1}/$1 [NE,R,L,QSA]

That forward slash tells the RewriteRule that it's working on requests to the root (public web root) directory.
0
 
HackneyCabCommented:
Oh, and you should use a capture in the RewriteCond so that you can use the result in the RewriteRule, something like this:

RewriteCond %{HTTP_HOST} (.+)
RewriteCond ${domains:%1} ^(.+)$
RewriteRule /(.*) http%{ENV:s}://%1/$1 [NE,R,L,QSA]

The new RewriteCond should force something to appear for the value of ${domains:%1} and then the (.+) should capture the entire contents of its value.

Then in the RewriteRule, the %1 should refer to the value of that captured RewriteCond pattern. So it should only have to do the domain lookup once.
0
 
RegProctorAuthor Commented:
Hm, I'll have to remember not to trust "apache2ctl -t" for checking syntax as I would have thought that a bracket and brace unmatched would have thrown a syntax error for sure. In any case, that of course fixed the lower casing problem.

I can't use RewriteCond ${domains:%1} ^(.+)$ because RewriteCond ${domains:%1} !^$ purpose is to say "if not blank" whereas the new form I think will always be true. However, you gave me an idea and we are much closer now because SETUP 1 below works.

However, SETUP 2 still gives an infinite loop.


----------------------------------------------------------------
SETUP 1
.htaccess
RewriteCond ${tolower:${domains:%{HTTP_HOST}}} (.*)
RewriteRule ^ - [E=domain:%1]

RewriteCond %{ENV:domain} !^$
RewriteRule (.*) http%{ENV:s}://%{ENV:domain}/$1 [NE,R,L,QSA]
----------------------------------------------------------------
SETUP 2

RewriteEngine On

RewriteMap domains txt:/etc/apache2/domains-mapping.txt
RewriteMap tolower int:tolower

RewriteCond %{HTTPS}s ^(on(s)|off)$
RewriteRule ^ - [E=s:%2]

RewriteCond ${tolower:${domains:%{HTTP_HOST}}} (.*)
RewriteRule ^ - [E=domain:%1]

RewriteCond %{ENV:domain} !^$
RewriteRule /(.*) http%{ENV:s}://%{ENV:domain}/$1 [NE,R,L,QSA]
----------------------------------------------------------------

Open in new window

0
Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
RegProctorAuthor Commented:
Setup 2 being in the httpd.conf file of course.
0
 
RegProctorAuthor Commented:
The looping is actually on the per-dir level according to the logs so it looks like the per-dir (.htaccess) is getting executed before the per-server or that the per-server lines just don't do anything.
0
 
HackneyCabCommented:
Well, ^(.+)$ is the same as !^$ because the .+ means "must contain at least one character (of any type)". So a blank string will not satisfy ^(.+)$, and you get to capture the value for use later.

As for the infinite loop, I have to admit that looking at your trio of conditions and rules, I'm not precisely sure what it is you're trying to do. Which cond/rule pair is it causing the infinite loop?
0
 
RegProctorAuthor Commented:
The .htaccess file has a whole host of rules I haven't shown here (for the CMS Drupal) and they aren't important to the problem. The reason for the loop is that I have a situation where:

Domain A & B are pointed to this server via DNS.
Domain A is in the Apache vhost file, domain B is not.

When the mapping works domain B gets mapped to domain A so the .htaccess stuff never sees the domain that Apache is not setup for. However, when the mapping doesn't work we get this loop condition -- which is fine because there should always be a valid domain.

The reason for this code is that it is becoming apparent that many clients (hopefully so to be clients as I am just getting started) are going to want multiple domains but only one as the web site so setting up a map that I can easily write a script to automatically append to is the ideal solution. And, once the whole thing is working I'll probably eventually replace the text file with a Berkeley, MySQL or some other database lookup.
0
 
RegProctorAuthor Commented:
Ah, in my haste I did ^(.*)$ instead of ^(.+)$, sorry

I like the ENV variable solution though so I'll probably keep it that way... fully acknowledging  you are right of course and it would work the way you showed it.
0
 
RegProctorAuthor Commented:
Hm, even better using what you did and what I was doing we can get it down to two lines.

Now I just need to get it to work in the httpd.conf file.


RewriteCond ${tolower:${domains:%{HTTP_HOST}}} (.+)
RewriteRule (.*) http%{ENV:s}://%1/$1 [NE,R,L,QSA]

Open in new window

0
 
HackneyCabCommented:
If that works in a .htaccess file, this should work in httpd.conf:

RewriteCond ${tolower:${domains:%{HTTP_HOST}}} (.+)
RewriteRule ^/(.*)$ http%{ENV:s}://%1/$1 [NE,R,L,QSA]

(I forgot the start and end symbols in the pattern earlier, which may have allowed it to grab only the final part of the path, after the final slash.)
0
 
RegProctorAuthor Commented:
Hm, that didn't help but at this point I'm wondering if it is something other than how the rule is written such as perhaps there is something extra that needs to be done to make it work on the server level.

For example I know in my version of Apache, 2.2, that you need to add "Options +FollowSymLinks" in the .htaccess for things to work right, something they added in some version as a security precaution. Maybe there is something needed on the config level (although the RewriteMap statement works so I couldn't imagine what). Or perhaps %{HTTP_HOST} isn't available at this stage or something.

Anyway, I'll try a hard coded redirect and see if that works and go from there (with tiny changes working towards the full generic version) and in the meantime if you have any ideas please let me know.

You were right in the first place with /(.*) as this will use the full string (being greedy by default) and ^$ are not needed to force the issue. If it was to capture just from the last slash of the string it would be /(.*?) (with the non-greedy modifier added).
0
 
HackneyCabCommented:
Just to check, you're not leaving these directives in .htaccess files at the same time as having them in httpd.conf are you? Having the same directives in both might well cause infinite looping.
0
 
RegProctorAuthor Commented:
Of course not.
0
 
RegProctorAuthor Commented:
The problem is that the instructions simply are not getting executed before the .htaccess or the HTTP_HOST is not set yet -- I'm not sure how to prove which. I showed this by putting this in the httpd.conf

RewriteCond %{HTTP_HOST} ^www\.domain-a\.com$
RewriteRule ^$ http://www.domain-b.com/ [NE,R,L]

And getting the same result. I even removed the .htaccess file and tried it again to see what would happen and got an "access forbidden" message, which is fair enough.

0
 
HackneyCabCommented:
Actually, thinking real hard about it, I can't work out why you're not getting infinite looping even in the .htaccess version.

Can you display the content of your domains mapping file (or an anonymised chunk of it)?

I'm trying to work out why the .htaccess version would stop.
0
 
RegProctorAuthor Commented:
Actually, since the rewritemap works which is set in the line above in the httpd.conf file when the .htaccess file is run I think it reasons that the HTTP_HOST variable is not set.
0
 
RegProctorAuthor Commented:
The rewritemap file is very simple:

www.domain-a.com www.domain-b.com
www.domain-c.com www.domain-d.com

and so on - so far it only has two lines.

0
 
HackneyCabCommented:
It's at the point now where I'm at a loss without being in front of the affected console. I can't grasp what it is that's causing you to suspect that HTTP_HOST is not set. And I can't understand what is changing after the .htaccess rewrite that stops it satisfying the same condition and rewriting again and again in a loop.

I think it's time to resort to log analysis. Find your Apache access log (on my box it lives at /usr/local/apache2/logs/access_log) and then pore over it to see exactly what is happening when you make a single request that causes trouble. (Ideally clear the log first, and make sure no other users can be making requests at the same time.)
0
 
RegProctorAuthor Commented:
I've been using the logs. One thing I have never found on Apache that would be great is the ability to write a custom message to the log (WriteToLog "My Var: " ${MyVar} for example).

Anyway, thanks. At this point it works (just not in the httpd.conf file) and it's not worth any more effort at this time... too many other things to take care of. And, I've often found in software that when you start working on something else the answers to previous unsolved mysteries often make themselves known... and that's a much easier way to solve things sometimes.
0
 
RegProctorAuthor Commented:
I fully understand needing to get on the server, no worries, you helped me get it working, that's the most important part. The last piece is more of nicety than a need.
0
 
HackneyCabCommented:
Glad to have been of some use. And hopefully you're right and the answer will fall into place sooner rather than later. If it does, add what you find here. Someone else is bound to find themselves with the same question one day.

Good luck.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 13
  • 8
Tackle projects and never again get stuck behind a technical roadblock.
Join Now