Set RewriteBase to the current folder path dynamically
If you have RewriteBase
command in htaccess, you might comment that and then it will be automatically resolved to that directory.
Building on anubhava's answer and Jon Lin's, here's what I just came up with for myself (haven't used this in production nor tested this extensively yet).
Let's use this example URL, where .htaccess is in current_folder:
http://localhost/path_to/current_folder/misc/subdir/file.xyz
Filesystem: /var/www/webroot/path_to/current_folder/.htaccess
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteCond %{ENV:SUBPATH} ^$ # Check if variable is empty. If it is, process the next rule to set it.
RewriteRule ^(.*)$ - [ENV=SUBPATH:$1]
# SUBPATH is set to 'misc/subdir/file.xyz'
RewriteCond %{ENV:CWD} ^$
RewriteCond %{ENV:SUBPATH}::%{REQUEST_URI} ^(.*)::(.*?)\1$
RewriteRule ^ - [ENV=CWD:%2]
# CWD is set to '/path_to/current_folder/'
RewriteCond %{ENV:FILENAME} ^$
RewriteCond %{REQUEST_URI} ^.*/(.*)$
RewriteRule ^ - [ENV=FILENAME:%1]
# FILENAME is set to 'file.xyz'
# Check if /var/www/webroot/path_to/current_folder/misc/subdir/file.xyz exists.
# -f checks if a file exists, -d checks for a directory.
# If it exists, rewrite to /path_to/current_folder/misc/subdir/file.xyz and stop processing rules.
RewriteCond %{ENV:SUBPATH} ^.+$ # Ensure SUBPATH is not empty
RewriteCond %{DOCUMENT_ROOT}%{ENV:CWD}%{ENV:SUBPATH} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{ENV:CWD}%{ENV:SUBPATH} -d
RewriteRule ^.*$ %{ENV:CWD}%{ENV:SUBPATH} [END]
# Check if /var/www/webroot/path_to/current_folder/file.xyz exists.
# If it exists, rewrite to /path_to/current_folder/file.xyz and stop processing rules.
RewriteCond %{ENV:FILENAME} ^.+$
RewriteCond %{DOCUMENT_ROOT}%{ENV:CWD}%{ENV:FILENAME} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{ENV:CWD}%{ENV:FILENAME} -d
RewriteRule ^.*$ %{ENV:CWD}%{ENV:FILENAME} [END]
# Else, rewrite to /path_to/current_folder/index.html and stop processing rules.
RewriteRule ^.*$ %{ENV:CWD}index.html [END]
You can view the details of what's happening for yourself by using LogLevel alert rewrite:trace6
in your httpd.conf
in apache, and then looking in your error.log
.
Here's a bit more clarification on the following two lines, which I'd still found a bit confusing.
RewriteCond %{ENV:SUBPATH}::%{REQUEST_URI} ^(.*)::(.*?)\1$
RewriteRule ^ - [ENV=CWD:%2]
First off, the double colon ::
is not an operator of any sort; it's just an arbitrary delimiter. The RewriteCond
expands TestString %{ENV:SUBPATH}::%{REQUEST_URI}
to the following:
misc/subdir/file.xyz::/path_to/current_folder/misc/subdir/file.xyz
Then our CondPattern ^(.*)::(.*?)\1$
:
^(.*)::
matchesmisc/subdir/file.xyz::
\1
is the first capture group,misc/subdir/file.xyz
(.*?)\1$
becomes(.*?)misc/subdir/file.xyz$
- Thus, our second capture group
(.*?)
matches the remaining/path_to/current_folder/
And our RewriteRule
sets CWD
to %2
, which is the second capture group of CondPattern.
Here is one way one can grab the RewriteBase
in an environment variable which you can then use in your other rewrite rules:
RewriteCond %{REQUEST_URI}::$1 ^(.*?/)(.*)::\2$
RewriteRule ^(.*)$ - [E=BASE:%1]
Then you can use %{ENV:BASE}
in your rules to denote RewriteBase
, i.e.:
#redirect in-existent files/calls to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . %{ENV:BASE}/index.php [L]
Explanation:
This rule works by comparing the REQUEST_URI
to the URL path that RewriteRule
sees, which is the REQUEST_URI
with the leading RewriteBase
stripped away. The difference is the RewriteBase
and is put into %{ENV:BASE}
.
- In a
RewriteCond
, the LHS (test string) can use back-reference variables e.g.$1
,$2
OR%1
,%2
etc but RHS side i.e. condition string cannot use these$1
,$2
OR%1
,%2
variables. - Inside the RHS condition part only back-reference 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 the
RewriteCond
first captured group is(.*?/)
. It will be represented by internal back-reference\1
. - As you can make 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 befoobar.php
.^(.*?/)(.*)::\2$
is putting the difference in 1st captured group%1
or\1
. For our example it will populate%1
and\1
with the value/directory/
which is used later in setting up env variable%{ENV:BASE}
inE=BASE:%1
.
The accepted solution did not work for me, I think, but this did: https://web.archive.org/web/20180401034514/http://www.zeilenwechsel.de/it/articles/8/Using-mod_rewrite-in-.htaccess-files-without-knowing-the-RewriteBase.html
Long story short:
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond $0#%{REQUEST_URI} ([^#]*)#(.*)\1$
RewriteRule ^.*$ %2index.php [QSA,L]