HWD HWD - 6 months ago 15
PHP Question

How Do I Use Apache RewriteMap with PHP?

I've been looking into using RewriteMap to dynamically handle a range of URL rewrites, and what I'd like to do is use a PHP script to generate the appropriate rewrite conditions and rules -- but I just can't get it working.

I've got Apache running on a Linux server with WebHost Manager and cPanel.

I added the following to WHM > Apache Configuration > Include Editor > PreVirtual Host Include > All Versions:

RewriteMap myrules "prg:/usr/lib/php /path/to/my/script.php"


I updated the config file and Apache restarted okay.

Now I'd like to call the RewriteMap I defined from an .htaccess file to execute the rewrite, but this is where I'm stuck and everything seems to break.

I'd like to grab the incoming domain, check it against the map, and return the associated domain:user_name value. Something like:

RewriteCond %{HTTP_HOST} ^(www\.)?${myrules:%{HTTP_HOST}}\.com$ [NC]
RewriteRule ^(.*)$ http://mywebsite.com/user/${myrules:%{HTTP_HOST}}/$1 [NC,P,L]


So, just for example, if my map has johnsmith.com:john-smith, then the incoming URL johnsmith.com would proxy for the content at mywebsite.com/user/john-smith/.

First, I'm pretty sure the cond/rule above is all wrong.

Getting it correct is my first priority.

Second, how would I output my map using the PHP script defined by the RewriteMap?

Any help would be appreciated!

Answer

Does johnsmith.com resolve to your server in the first place? If johnsmith.com and mywebsite.com are handled by the same server, you don't need to proxy the request.

I think your rule should be more like this:

RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+\.com)$ [NC]
RewriteRule ^.*$ http://mywebsite.com/user/${myrules:%1}/$0 [NC,P,L]
# or
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+\.com)$ [NC]
RewriteRule ^ - [E=MAPTO:${myrules:%1}]
RewriteCond %{ENV:MAPTO} !=""
RewriteRule ^.*$ http://mywebsite.com/user/%{ENV:MAPTO}/$0 [NC,P,L]

Notes:

  • $0 is the whole string matched by the RewriteRule's pattern (if the .htaccess is in your DocumentRoot, $0 and %{REQUEST_URI} are the same)
  • %1 is the string matched by the first capturing parentheses of the last RewriteCond (so, the value of the Host header without the potential prefix "www.")

On the second set of rules, the first rule put the result of the "RewriteMap call" in a environment variable called MAPTO. The second one, is intended to ignore unknown/non-mapped host (MAPTO valued to the empty string).

And an illustrating PHP code as RewriteMap:

<?php
$map = [
    'foo.com' => 'bar',
    'johnsmith.com' => 'john-smith',
];

if (!defined('STDOUT'))
    define('STDOUT', fopen('php://stdout', 'w'));
if (!defined('STDIN'))
    define('STDIN', fopen('php://stdin', 'r'));
set_file_buffer(STDOUT, 0);
while (!feof(STDIN)) {
    $input = rtrim(fgets(STDIN), "\n");
    fputs(STDOUT, (array_key_exists($input, $map) ? $map[$input] : 'NULL') . "\n");
}

Note: the 'NULL' value is the string expected by Apache to know when a value is not mapped to another one. It allows you to define your own default value at Apache's level. eg: to have "root" as fallback, use the following syntax: ${myrules:%1|root}.