Help understanding rewrite log (want to internally rewrite a page when requested from specific HTTP_HOST) - apache

I have a Drupal site, site.com, and our client has a campaign that they're promoting for which they've bought a new domain name, campaign.com. I'd like it so that a request for campaign.com internally rewrites to a particular page of the Drupal site. Note Drupal uses an .htaccess file in the document root.
The normal Drupal rewrite is
# Rewrite URLs of the form 'x' to the form 'index.php?q=x'.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
I added the following before the normal rewrite.
# Custom URLS (eg. microsites) go here
RewriteCond %{HTTP_HOST} =campaign.com
RewriteCond %{REQUEST_URI} =/
RewriteRule ^ index.php?q=node/22 [L]
Unfortunately it doesn't work, it just shows the homepage. Turning on the rewrite log I get this.
1. [rid#2da8ea8/initial] (3) [perdir D:/wamp/www/] strip per-dir prefix: D:/wamp/www/ ->
2. [rid#2da8ea8/initial] (3) [perdir D:/wamp/www/] applying pattern '^' to uri ''
3. [rid#2da8ea8/initial] (2) [perdir D:/wamp/www/] rewrite '' -> 'index.php?q=node/22'
4. [rid#2da8ea8/initial] (3) split uri=index.php?q=node/22 -> uri=index.php, args=q=node/22
5. [rid#2da8ea8/initial] (3) [perdir D:/wamp/www/] add per-dir prefix: index.php -> D:/wamp/www/index.php
6. [rid#2da8ea8/initial] (2) [perdir D:/wamp/www/] strip document_root prefix: D:/wamp/www/index.php -> /index.php
7. [rid#2da8ea8/initial] (1) [perdir D:/wamp/www/] internal redirect with /index.php [INTERNAL REDIRECT]
8. [rid#2da7770/initial/redir#1] (3) [perdir D:/wamp/www/] strip per-dir prefix: D:/wamp/www/index.php -> index.php
9. [rid#2da7770/initial/redir#1] (3) [perdir D:/wamp/www/] applying pattern '^' to uri 'index.php'
10.[rid#2da7770/initial/redir#1] (3) [perdir D:/wamp/www/] strip per-dir prefix: D:/wamp/www/index.php -> index.php
11.[rid#2da7770/initial/redir#1] (3) [perdir D:/wamp/www/] applying pattern '^(.*)$' to uri 'index.php'
12.[rid#2da7770/initial/redir#1] (1) [perdir D:/wamp/www/] pass through D:/wamp/www/index.php
I'm not used to mod_rewrite, so I might be missing something, but comparing the logs from a call to http://site.com/node/3 and from http://campaign.com/ I can't see any meaningful difference. Specifically uri and args on line 4 seem correct, the internal redirect on line 7 seems right, and the pass through on line 12 seems right (because the file index.php exists). But for some reason it seems the query string's been discarded/ignored around the time of the internal redirect. I'm completely stumped.
Also, if anyone could provide a reference on understanding the rewrite log, that might help. It'd be great if there's a way to track the query string through the internal redirect.
FWIW I'm using WampServer 2.1 with Apache 2.2.17.

Thanks for asking this question, it's something that I need to do too. I don't know the way to do this by means of the .htaccess, and hope that someone here can answer that.
But I do the same thing by using Drupal's menu system with this code in a custom module:
function mymodule_menu() {
$items = array();
$items['domain_redirect'] = array(
'page callback' => 'domain_redirect',
'type' => MENU_NORMAL_ITEM,
'access arguments' => array('access content'),
);
return $items;
}
function domain_redirect() {
switch ($_SERVER['SERVER_NAME'])
{
case "campaign.com":
$goto = "node/22";
break;
default:
$goto = "/";
}
drupal_goto($goto);
}
Then set the frontpage to domain_redirect.

Related

Send all requests to subfolder using Apache

I'm trying to get all requests to my website passed on to a subfolder in my webroot.
Here's my folder structure:
/webroot
current
releases
release1
index.html
en
index.html
release2
index.html
en
index.html
...
.htaccess
The current folder is a symlink pointing to a folder in the releases folder. The idea is that when I make a new release, a new folder is created in the releases folder and the current symlink is then pointing to this new folder.
In my .htaccess I'm then trying to pass on all requests to the current symlink. So if a user requests, for example, /en the requests should be routed through the symlink and while the browser URL will look like https://example.com/en the actual request will end up at https://example.com/current/en
Now, this works perfectly as long as the request ends with a trailing slash, for example https://example.com/en/ but if I remove the trailing slash the routing works BUT the URL in the browser will be https://example.com/current/en
Here's the rewrites I'm doing in my .htaccess:
Options +FollowSymlinks -MultiViews -Indexes
DirectorySlash On
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_URI} !^/current/
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.*)$ current/$1 [L]
And here's the logfile
[perdir /path/to/webroot/] applying pattern '^(.*)$' to uri 'en'
[perdir /path/to/webroot/] RewriteCond: input='/en' pattern='!^/current/' => matched
[perdir /path/to/webroot/] RewriteCond: input='' pattern='^$' => matched
[perdir /path/to/webroot/] rewrite 'en' -> 'current/en'
[perdir /path/to/webroot/] add per-dir prefix: current/en -> /path/to/webroot/current/en
[perdir /path/to/webroot/] trying to replace prefix /path/to/webroot/ with /
strip matching prefix: /path/to/webroot/current/en -> current/en
add subst prefix: current/en -> /current/en
[perdir /path/to/webroot/] internal redirect with /current/en [INTERNAL REDIRECT]
[perdir /path/to/webroot/] strip per-dir prefix: /path/to/webroot/current/en -> current/en
[perdir /path/to/webroot/] applying pattern '^(.*)$' to uri 'current/en'
[perdir /path/to/webroot/] RewriteCond: input='/current/en' pattern='!^/current/' => not-matched
[perdir /path/to/webroot/] pass through /path/to/webroot/current/en
[perdir /path/to/webroot/] strip per-dir prefix: /path/to/webroot/current/en/ -> current/en/
[perdir /path/to/webroot/] applying pattern '^(.*)$' to uri 'current/en/'
[perdir /path/to/webroot/] RewriteCond: input='/current/en/' pattern='!^/current/' => not-matched
[perdir /path/to/webroot/] pass through /path/to/webroot/current/en/
[perdir /path/to/webroot/] strip per-dir prefix: /path/to/webroot/current/en/index.php -> current/en/index.php
[perdir /path/to/webroot/] applying pattern '^(.*)$' to uri 'current/en/index.php'
[perdir /path/to/webroot/] RewriteCond: input='/current/en/index.php' pattern='!^/current/' => not-matched
[perdir /path/to/webroot/] pass through /path/to/webroot/current/en/index.php
[perdir /path/to/webroot/] strip per-dir prefix: /path/to/webroot/current/en/index.html -> current/en/index.html
[perdir /path/to/webroot/] applying pattern '^(.*)$' to uri 'current/en/index.html'
[perdir /path/to/webroot/] RewriteCond: input='/current/en/index.html' pattern='!^/current/' => not-matched
[perdir /path/to/webroot/] pass through /path/to/webroot/current/en/index.html
but if I remove the trailing slash the routing works BUT the URL in the browser will be https://example.com/current/en
I assume you mean https://example.com/current/en/ (with a trailing slash).
This happens because mod_dir tries to "fix" (with a 301 redirect) the URL by appending a trailing slash to physical directories when omitted (this is necessary for directory indexes to work correctly). This "fix" occurs after the URL has been rewritten to the subdirectory (since the URL-path /en does not initially map to a subdirectory).
You can disable this behaviour of mod_dir, so that trailing slashes are not appended; but that gets messy (you then need to manually append the trailing slash as required) and could potentially raise security issues.
Instead, you need to make sure you are always linking to the URL with a trailing slash and manually correcting any URL that omits the trailing slash in .htaccess before mod_dir does so.
Your existing rewrite that rewrites to the /current subdirectory looks OK, although you don't necessarily need to check that the request is not already for /current/ (the first condition). (By checking that the request does not already start /current/ then you are allowing direct requests to the actual symlink'd filesystem location - although maybe that's a requirement?)
So, before your existing rewrite, add the following redirect:
# Fix any requests for directories that omit the trailing slash
RewriteCond %{DOCUMENT_ROOT}/current/$1 -d
RewriteRule ^(.+[^/])$ /$1/ [R=301,L]
Given a request for /example, the above first checks whether /current/example exists as a directory (albeit a symlink'd directory). If if does then it issues a 301 redirect to append the trailing slash to the original URL, not the rewritten URL. eg. /example is redirected to /example/, which is then later rewritten to /current/example/.
Test first with 302 (temporary) redirects to avoid potential caching issues.
You will need to clear your browser cache, since the erroneous 301 (permanent) redirect by mod_dir will have been cached by the browser.
UPDATE:
it should not be possible to directly access any files or folders in the "current" folder. Currently, that's possible...
As hinted at above, you could simply remove the first condition that checks whether the REQUEST_URI does not already start with /current/. So all direct requests are unconditionally rewritten to /current/. A request for /current/en/ would therefore be rewritten to /current/current/en, resulting in a 404.
However, a user could still potentially request the filesystem location directly, bypassing the symlink entirely. eg. /releases/release1/en/.
To block both these locations from direct access you could add the following as the first rule:
# Block direct access to "/current/" and "/releases/"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(current|releases)($|/) - [F]
Requesting /current/... or /releases/... directly would result in a "403 Forbidden". Or change the F to R=404 to serve a "404 Not Found" instead (no redirect occurs, despite the use of the R flag).
The check against the REDIRECT_STATUS environment variable (as in your original rule) ensures that the request can still be internally rewritten to these locations. (The REDIRECT_STATUS env var is empty on the initial request from the client, but set to the HTTP response status (eg. "200") after the first rewrite.)

How to redirect URL with htaccess (RewriteRule) and prevent direct access

using .htaccess I'd like to transparently redirect requests for folder "old" to folder "new", and in the same time prevent direct access to folder "new":
desired result:
http://example.com/old/... -> will display what's in "new" (no URL change in browser!)
http://example.com/new/... -> no access
this is my code in .htaccess (the 1st line is here because several domains share the same root folder):
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^old(.*)$ new$1 [L]
RewriteRule ^new(.*)$ - [F]
Well, what happens is that the 3d line triggers because of the substitution in the 2nd. I was convinced that the flag "L" would prevent this from happening (end of processing), but it seems that's not the case.
Do you have any suggestions what needs to be done (I tried to debug with rewrite log, but without success)?
I did some logging and found the following:
[rid#d0ac98/initial] (3) [per-dir C:/www/example/] add path info postfix: C:/www/example/new -> C:/www/example/new/
[rid#d0ac98/initial] (3) [per-dir C:/www/example/] strip per-dir prefix: C:/www/example/new/ -> new/
[rid#d0ac98/initial] (3) [per-dir C:/www/example/] applying pattern '^new(.*)$' to uri 'new/'
[rid#d0ac98/initial] (4) RewriteCond: input='localhost' pattern='^example\.com$' => not-matched
[rid#d0ac98/initial] (4) RewriteCond: input='localhost' pattern='^localhost$' => matched
[rid#d0ac98/initial] (2) [per-dir C:/www/example/] rewrite new/ -> old/
[rid#d0ac98/initial] (3) [per-dir C:/www/example/] add per-dir prefix: old/ -> C:/www/example/old/
[rid#d0ac98/initial] (2) [per-dir C:/www/example/] strip document_root prefix: C:/www/example/old/ -> /example/old/
[rid#d0ac98/initial] (1) [per-dir C:/www/example/] internal redirect with /example/old/ [INTERNAL REDIRECT]
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] strip per-dir prefix: C:/www/example/old/ -> old/
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] applying pattern '^new(.*)$' to uri 'old/'
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] strip per-dir prefix: C:/www/example/old/ -> old/
[rid#d217a8/initial/redir#1] (3) [per-dir C:/www/example/] applying pattern '^old(.*)$' to uri 'old/'
[rid#d217a8/initial/redir#1] (2) forcing 'C:/www/example/old/' to be forbidden
This seems an internal redirect, which causes the "forbidden" result. Indeed, the documentation mentions it:
It is therefore important, if you are using RewriteRule directives in one of these contexts, that you take explicit steps to avoid rules looping, and not count solely on the [L] flag to terminate execution of a series of rules, as shown below. An alternative flag, [END], can be used to terminate not only the current round of rewrite processing but prevent any subsequent rewrite processing from occurring in per-directory (htaccess) context. This does not apply to new requests resulting from external redirects...
So I suppose that in my example the error was due to the fact that I used "L" flag instead the "END" flag?
I found an alternative solution (3rd line is inserted here):
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^old(.*)$ new$1 [L]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^new(.*)$ - [F]
The 4th line will be executed only if there's no internal redirect.
You can use:
RewriteEngine On
# if directly requesting /new then block it
RewriteCond %{THE_REQUEST} \s/+new(/\S*)?\s [NC]
RewriteRule ^ - [F]
# forward /old/abc to /new/abc
RewriteCond %{HTTP_HOST} ^(www\.)?example\.com$ [NC]
RewriteRule ^old(/.*)?$ new$1 [L,NC]
We used THE_REQUEST in first rule. THE_REQUEST variable represents original request received by Apache from your browser and it doesn't get overwritten after execution of some rewrite rules. REQUEST_URI on the other hand changes its value after other rewrite rules.
For the same reason your rule RewriteRule ^new(.*)$ - [F] will even block your request from /old/ since first rule changes URI to /new/.

Why doesn't the RewriteCond prevent the RewriteRule from processing?

I've got the following in my .conf file used for cache busting js:
<Directory "/www/virtual/site/html/js/">
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^\d+\/(.+)$ $1 [L]
</Directory>
The idea here is to rewrite site.com/js/123456/script.js to site.com/js/script.js
(The rewriting works just fine)
If I look at the rewrite log the part I'm not understanding is that it is applying the RewriteRule to the script after the interal redirect... Shouldn't the %{REQUEST_FILENAME} !-f prevent it from processing the RewriteRule since the file exists?
Rewrite Log
[rid#2b607f8f2dc8/initial] (3) [perdir /www/virtual/site2/html/js/] add path info postfix: /www/virtual/site2/html/js/1234 -> /www/virtual/site2/html/js/1234/script.js
[rid#2b607f8f2dc8/initial] (3) [perdir /www/virtual/site2/html/js/] strip per-dir prefix: /www/virtual/site2/html/js/1234/script.js -> 1234/script.js
[rid#2b607f8f2dc8/initial] (3) [perdir /www/virtual/site2/html/js/] applying pattern '^\d+\/(.+)$' to uri '1234/script.js'
[rid#2b607f8f2dc8/initial] (2) [perdir /www/virtual/site2/html/js/] rewrite '1234/script.js' -> 'script.js'
[rid#2b607f8f2dc8/initial] (3) [perdir /www/virtual/site2/html/js/] add per-dir prefix: script.js -> /www/virtual/site2/html/js/script.js
[rid#2b607f8f2dc8/initial] (2) [perdir /www/virtual/site2/html/js/] strip document_root prefix: /www/virtual/site2/html/js/script.js -> /js/script.js
[rid#2b607f8f2dc8/initial] (1) [perdir /www/virtual/site2/html/js/] internal redirect with /js/script.js [INTERNAL REDIRECT]
[rid#2b6073eb14a8/initial/redir#1] (3) [perdir /www/virtual/site2/html/js/] strip per-dir prefix: /www/virtual/site2/html/js/script.js -> script.js
[rid#2b6073eb14a8/initial/redir#1] (3) [perdir /www/virtual/site2/html/js/] applying pattern '^\d+\/(.+)$' to uri 'script.js'
[rid#2b6073eb14a8/initial/redir#1] (1) [perdir /www/virtual/site2/html/js/] pass through /www/virtual/site2/html/js/script.js
mod_rewrite (by design) keeps on applying rewrite-rules, till the url no longer changes.
So the first time it rewrite 1234/script.js to script.js the second time it tries to apply the rule, the pattern regular expression doesn't match and script.js is 'passed through'.
This is the way it is supposed to work. It can sometimes be a pain, but it makes mod_rewrite a lot more powerful/useful.
edit
mod_rewite first tests the RewriteRule pattern, before trying to apply the RewriteCond's. This is so you can use backreferences $0, $1 etc. in your RewriteCond. After all the RewriteCond's it will create the substitution string, so it can also use backrefereces %0, %1. etc. See also the diagram below.
So this is why it tried to apply the pattern before testing if the file exists.

Apache mod_rewrite a subdomain to a subfolder (via internal redirect)

I'm trying to write a set of mod_rewrite rules that allow my users to utilize a single folder for doing development on different projects, and not have to mess with adding vhosts for every single project.
My idea to accomplish this, is to set up a "Global VHost" for every single user who needs this ability (only 3-4), the vhost would be something like: .my-domain.com. From there, I want to promote my users to write code as if it were on a domain, and not in a sub folder. For example, if bob was working on a project named 'gnome,' I'd like the URL bob (and anyone else on our internal network) loads to get to this project to be: http://gnome.bob.my-domain.com. But, what I'd like Apache to do, is recognize that "gnome" is a "project" and thus map the request, internally, to bob.my-domain.com/gnome/.
I've got what I thought would work, and it's quite simple, but..it doesn't work! The request just goes into an infinite loop and keeps prefixing the sub domain onto the re-written request URI.
The mod rewrite code i have is:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^([^.]+)\.bob\.my-domain\.com
RewriteCond %{REQUEST_URI} !^/%1.*
RewriteRule ^(.*)$ /%1/$1 [L]
I've googled around a bit about this, but I've yet to find any real solutions that work. Has anyone tried this - or maybe, does anyone have a better idea? One that doesn't involve making a virtual host for every project (I've got designers..I think everyone would agree that a designer shouldn't be making virtual hosts..)
Thanks!
Here is a snippet from the rewrite_log:
[rid#838dc88/initial] (3) [perdir /home/bob/http/] strip per-dir prefix: /home/bob/http/index.html -> index.html
[rid#838dc88/initial] (3) [perdir /home/bob/http/] applying pattern '^(.*)$' to uri 'index.html'
[rid#838dc88/initial] (4) [perdir /home/bob/http/] RewriteCond: input='gnome.bob.my-domain.com' pattern='^([^.]+)\.bob\.my-domain\.com' => matched
[rid#838dc88/initial] (4) [perdir /home/bob/http/] RewriteCond: input='/index.html' pattern='!^/%1.*' => matched
[rid#838dc88/initial] (2) [perdir /home/bob/http/] rewrite 'index.html' -> '/gnome/index.html'
[rid#838dc88/initial] (1) [perdir /home/bob/http/] internal redirect with /gnome/index.html [INTERNAL REDIRECT]
[rid#8392f30/initial/redir#1] (3) [perdir /home/bob/http/] strip per-dir prefix: /home/bob/http/gnome/index.html -> gnome/index.html
[rid#8392f30/initial/redir#1] (3) [perdir /home/bob/http/] applying pattern '^(.*)$' to uri 'gnome/index.html'
[rid#8392f30/initial/redir#1] (4) [perdir /home/bob/http/] RewriteCond: input='gnome.bob.my-domain.com' pattern='^([^\.]+)\.bob\.my-domain\.com' => matched
[rid#8392f30/initial/redir#1] (4) [perdir /home/bob/http/] RewriteCond: input='/gnome/index.html' pattern='!^/%1.*' => matched
[rid#8392f30/initial/redir#1] (2) [perdir /home/bob/http/] rewrite 'gnome/index.html' -> '/gnome/gnome/index.html'
[rid#8392f30/initial/redir#1] (1) [perdir /home/bob/http/] internal redirect with /gnome/gnome/index.html [INTERNAL REDIRECT]
[rid#8397970/initial/redir#2] (3) [perdir /home/bob/http/] add path info postfix: /home/bob/http/gnome/gnome -> /home/bob/http/gnome/gnome/index.html
This is just a snippet, there are a few 10s or 100 or so lines of apache basically rewriting /gnome/index.html to /gnome/gnome/gnome/gnome/gnome/index.html, etc before apache hits its rewrite limit, gives up, and throws error 500
After a few years of ignoring this problem and coming back to it at various points, I finally found a workable solution.
RewriteEngine on
RewriteCond %{HTTP_HOST} ^([^.]+)\.bob\.my-domain\.com
RewriteCond %1::%{REQUEST_URI} !^(.*?)::/\1/
RewriteRule ^(.*)$ /%1/$1 [L]
What I found was that back-references for previous RewriteCond directions are not available in the ConditionPattern parameter of future RewriteConditions. If you want to use a back-reference from a previous RewriteCond directive, you can only use it in the TestString parameter.
The above directives prepend the sub-domain matched in the 1st RewriteCond directive to the RequestURI, delimited by ::. What we then do in the RewriteCond Test String (regex) is re-capture the sub-domain name, then check to make sure our actual RequestURI doesn't begin with that sub-domain as a folder using a back reference within the same regex.
This sounds a lot more confusing than it really is, and I can't take the credit for discovering the answer. I found the answer as a response to another question here, %N backreference inside RewriteCond. Thanks to Jon Lin for answering that question, and unknown to him, my question too!
You might want to check
http://httpd.apache.org/docs/2.2/vhosts/mass.html
it deals with the DocumentRoot problem that you were experiencing.
Rule goes something like this
VirtualDocumentRoot /var/www/%1/
You can change the %1 for whatever suits you (http://httpd.apache.org/docs/2.0/mod/mod_vhost_alias.html)
Cheers
Some Questions:
You said "map internally" -- do you NOT want to use a redirect?
Are you using the same VirtualHost for gnome.bob.mysite.com and bob.mysite.com
Did you remember to create a ServerAlias for *.bob.mysite.com?
Here is a rough version that you could modify to work. It will capture the subdomain and requested URL, and do a redirect to the main domain with the subdomain as the first part of the path, followed by the requested path, followed by the query string.
ServerName www.mysite.com
ServerAlias *.mysite.com
RewriteCond %{HTTP_HOST} ^([a-zA-Z0-9-]+)\\.mysite.com$
RewriteRule ^/(.*) http://www.mysite.com/%1/$1 [R=301,L]',
Have you tried using another rewrite rule to process the one before it?
RewriteEngine On
RewriteCond %{HTTP_HOST} ^([^.]+)\.bob\.my-domain\.com
RewriteCond %{REQUEST_URI} !^/%1.*
RewriteRule ^(.*)$ /%1/$1 [C]
RewriteRule ^/(.*)\.bob\.my-domain\.com/(.*) /$1/$2 [L]
But I think your bigger problem is the fact that your server doesn't understand it is getting served under a different name.
It thinks it is running in the /gnome/ directory while the browser things it is running in the / directory. So any relative URL's that you have are going to cause issues.
What you need is a filter that will run all the URL's in your page through a processor and change them from /gnome/ to /.

A mod_rewrite problem

I'm trying to implement the following mod_rewrite rule:
host.com/developer/ => host.com/developer/index.py
host.com/developer/branchX => host.com/developer/index.py?branch=branchX
host.com/developer/branchX/commitY => host.com/developer/index.py?branch=branchX&commit=commitY
Currently, the appropriate config section looks like this:
Options FollowSymLinks
AllowOverride None
RewriteEngine on
RewriteRule ^([^/]+)$ /$1/index.py [L]
RewriteRule ^([^/]+)/([^/]+) /$1/index.py?branch=$2 [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)$ /$1/index.py?branch=$2&commit=$3 [L]
However, after the URL has been initially rewritten, an internal redirect occurs and the URL is being rewritten again, ruining it. The process repeats many times and eventually results into a 500 error. The log (timestamps and perdir parts removed):
[..initial] (3) strip per-dir prefix: /home/www/host.com/master/a -> master/a
[..initial] (3) applying pattern '^([^/]+)$' to uri 'master/a'
[..initial] (3) strip per-dir prefix: /home/www/host.com/master/a -> master/a
[..initial] (3) applying pattern '^([^/]+)/([^/]+)' to uri 'master/a'
[..initial] (2) rewrite 'master/a' -> '/master/index.py?branch=a'
[..initial] (3) split uri=/master/index.py?branch=a -> uri=/master/index.py, args=branch=a
[..initial] (1) internal redirect with /master/index.py [INTERNAL REDIRECT]
[..initial/redir#1] (3) strip per-dir prefix: /home/www/host.com/master/index.py -> master/index.py
[..initial/redir#1] (3) applying pattern '^([^/]+)$' to uri 'master/index.py'
[..initial/redir#1] (3) strip per-dir prefix: /home/www/host.com/master/index.py -> master/index.py
[..initial/redir#1] (3) applying pattern '^([^/]+)/([^/]+)' to uri 'master/index.py'
[..initial/redir#1] (2) rewrite 'master/index.py' -> '/master/index.py?branch=index.py'
[..initial/redir#1] (3) split uri=/master/index.py?branch=index.py -> uri=/master/index.py, args=branch=index.py
How can I fix my rules to prevent the endless internal redirects?
Thanks.
The problem is that the url you are rewriting to is matched in the next pass. L specifies the last rule but only for this execution of the rules, URLs are processed again under certain circumstances (in this case, internal redirects). The solution is to add a RewriteCond to prevent looping, like this:
RewriteEngine on
RewriteCond %{REQUEST_URI} !index\.py
RewriteRule ^([^/]+)$ /$1/index.py [L]
RewriteCond %{REQUEST_URI} !index\.py
RewriteRule ^([^/]+)/([^/]+) /$1/index.py?branch=$2 [L]
RewriteCond %{REQUEST_URI} !index\.py
RewriteRule ^([^/]+)/([^/]+)/([^/]+)$ /$1/index.py?branch=$2&commit=$3 [L]