26

Is there any way to set RewriteBase to the path current folder (the folder which the .htaccess file is in) relative to the host root?

I have a CMS and if I move it to the directory in my host it does not work unless I set the RewriteBase to the path of directory relative to the root of host. I would like my CMS to work with only copy and paste, without changing any code in htaccess.

Update:

For example:

webroot

 - sub_directory
 - cms
 - .htaccess

in this case I should write in the htaccess: RewriteBase /

and if I move the htaccess inside sub_directory I should change RewriteBase to:

RewriteBase /sub_directory/

So I want something like

RewriteBase /%{current_folder}/

hi im zvaehn
  • 91
  • 1
  • 10
Eeliya
  • 1,534
  • 5
  • 23
  • 37

4 Answers4

49

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 be foobar.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} in E=BASE:%1.
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Can these two commands be altered so that they will match URLs that contain multiple forward slashes? For example this will not work: /directory/a///b/c/ (note the 3 slashes after a) – Razvan May 21 '15 at 14:10
  • 1
    Yes using a redirect rule all multiple slashes should be trimmed to single `/` first. – anubhava May 21 '15 at 14:12
  • 2
    Today, more than five years later you may have saved my sanity. Thank you! This started to devour my soul! – Wolfone Mar 23 '20 at 10:49
15

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]
Dakusan
  • 6,504
  • 5
  • 32
  • 45
  • 1
    Man, you saved my life, this works 100% and its short unlike the upper. If I could give my thumbs. – FantomX1 Jan 18 '18 at 17:52
  • 1
    I have changed the link to use archive.org . For future proofing: https://www.castledragmire.com/misc/HTAccessPost.png – Dakusan Feb 07 '20 at 05:28
5

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$:

  1. ^(.*):: matches misc/subdir/file.xyz::
  2. \1 is the first capture group, misc/subdir/file.xyz
  3. (.*?)\1$ becomes (.*?)misc/subdir/file.xyz$
  4. 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.

aplum
  • 188
  • 1
  • 8
1

If you have RewriteBase command in htaccess, you might comment that and then it will be automatically resolved to that directory.

T.Todua
  • 53,146
  • 19
  • 236
  • 237