mod_rewrite (Apache 2.4) RewriteBase outside htdocs - apache

I stuck on a problem trying to get a my rewrite rules working.
Assume the following setup:
Folder structure like this:
app/
web/ <--- Document-Root
.htaccess <--- My mod_rewrite rules
index.php
vendor/
company/
package/
Folder/
Route.php
The vhost points to "web/" as Document-Root and the security is reduced
so all overrides are allowed:
<Directory "~PATH~\web">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order deny,allow
allow from all
Require all granted
</Directory>
Now i try to rewrite simply every request (*) to "Route.php" (internal! no 301 or something).
For my understanding if if would call "www.example.com/a/b" then i would expect that the
request is internal redirected to ~PATH~/vendor/vendor/company/package/Folder/Route.php
where i can fetch everything and take the request in PHP for further processing.
But the rewrite fails. Maybe its not possible to traverse a directory up "/../" ?!
Content of .htacces:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /../vendor/company/package/Folder
RewriteRule .* - [E=ROUTER:Route.php]
# route calls to router /x/y/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . %{env:ROUTER}?mode=htaccess [QSA,L]
</IfModule>
Any help appreciated :)
Thanks in advance!

Try this
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule .* ../vendor/company/package/Folder/Router.php [L]
</IfModule>
EDIT:
I've tried it on my local machine.
This Setup works:
RewriteRule .* vendor/company/package/Folder/Router.php [L]
Seems like its not allowed to forward the Request to directories higher than the Docroot.
I've found this post:
htaccess RewriteRule redirecting to parent directory?
The important Part:
You can't rewrite to outside the document-root. This is a security thing.
EDIT2:
More Information: mod_rewrite - Apache HTTP Server Version 2.4
Quote:
URL-path
A DocumentRoot-relative path to the resource to be served. Note that mod_rewrite tries to guess whether you have specified a file-system path or a URL-path by checking to see if the first segment of the path exists at the root of the file-system. For example, if you specify a Substitution string of /www/file.html, then this will be treated as a URL-path unless a directory named www exists at the root or your file-system (or, in the case of using rewrites in a .htaccess file, relative to your document root), in which case it will be treated as a file-system path. If you wish other URL-mapping directives (such as Alias) to be applied to the resulting URL-path, use the [PT] flag as described below.

Related

How does .htaccess behave when located in directory above doc root

I have the following rules in my .htacess file. They work perfectly fine as is, but I have several doc roots which I would like to apply the same rules to, all in the same parent directory. Is this possible with .htaccess?
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/some/path($|/)
RewriteRule (.*) /some/path/$1 [L]
FallbackResource /some/path/index.html
I know .htaccess is somewhat relative to the directory they're in, so I'm thinking I need to modify these somehow.
The end result from just moving it directly to the parent directory is everything returns the contents of /some/path/index.html
For anyone this helps, I accomplished this by adding it to the "post virtual hosts" section of my main apache config.
<Directory "/home/*/parentdirectory/*">
RewriteEngine on
RewriteRule !/some/path/ /some/path%{REQUEST_URI} [L]
FallbackResource /some/path/index.html
</Directory>

How to avoid the need of typing .php on the url?

I'm on MacOs Big Sur, using Apache and PHP. What I want is: not needing to put .php on the end of my files to load it.
For instance, instead of typing this on the URL:
127.0.0.1/public_html/home.php
I want just to type
127.0.0.1/public_html/home
To achieve this, I'm using this code in .htaccess:
RewriteEngine On
Options -Indexes
DirectoryIndex home.php index.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L]
The code above works on my hosting, but for some reason, it does not work on my development machine. Instead, a get a 404 error.
The .htaccess file with the code is on the root of public_html folder.
What am I missing?
By typing some "nonsense" at the top of the .htaccess file and not getting an error (ordinarily you would get a 500 Internal Server Error) it would seem that .htaccess overrides were not enabled on the server. So, .htaccess files were effectively disabled - which they are by default on Apache 2.4.
To enable .htaccess overrides (to allow .htaccess to override the server config) you need to set the AllowOverride directive in the appropriate <Directory> container in the server config (or <VirtualHost> container). The default on Apache 2.4 is AllowOverride None.
With the directives as posted you would need a minimum of:
AllowOverride FileInfo Indexes Options
FileInfo for mod_rewrite, Indexes for DirectoryIndex and Options for Options and related directives.
Although it is common (and easier) to just set:
AllowOverride All
Reference:
https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L]
These directives are not strictly correct. Whilst they may work OK for the URLs you are testing, they would result in a rewrite-loop (500 error response) if you simply append a slash to your URLs (and there is no directory by that name), eg. /home/ (or /home/<anything>). This is because your condition that tests for the presence of the .php file is not necessarily the same as the URL-path you are rewriting to. See my answer to the following question on ServerFault for a thorough explanation of this issue: https://serverfault.com/questions/989333/using-apache-rewrite-rules-in-htaccess-to-remove-html-causing-a-500-error
Also, there's no need to check that the request does not map to a directory to then check if the request + .php extension maps to a file. If the request maps to a file then it can not also be a directory, so if the 2nd condition is true, the 1st condition must also be true and is therefore superfluous.
And there's no need to backslash-escape literal dots in the RewriteCond TestString - this is an "ordinary" string, not a regex.
So, these directives should be written like this instead:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule (.+) $1.php [L]
(RewriteBase should not be used here.)
You can further optimise this by excluding requests that already contain what looks like a file extension (assuming your URLs that need rewriting do not contain a dot near the end of the URL-path). For example:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.php [L]
(With this 2nd version, it does not matter if RewriteBase is set - it is not used.)
DirectoryIndex home.php index.php
You gave an example URL of /public_html/home (to which .php is appended). However, this DirectoryIndex directive allows home.php to also be served when simply requesting the directory /public_html/. It should be one or the other, not both.

AllowOverride and RewriteCond/RewriteRule interactions

I'm trying to set up a site running on Apache 2.4.16 to redirect all www URLs to non-www URLs. I'm using HTML5 Boilerplate's Apache configs to do this (as well as everything else they provide).
https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess
This happens on line 380, seen below:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ %{ENV:PROTO}://%1%{REQUEST_URI} [R=301,L]
</IfModule>
I'm using Include to add the whole file to my vhost config for the site, as well as an AllowOverride All for another .htaccess file at my doc root (same one that comes with Laravel 5):
production.vhost.conf (relevant part)
<Directory /var/www/hostname/production>
AllowOverride All
# Include H5BP server configs
Include server-configs-apache/dist/.htaccess
</Directory>
.htaccess (at doc root)
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
RewriteEngine On
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Now, almost everything from H5BP's .htaccess was working, except for the redirect from www to non-www. After poking around I noticed that the redirect was only working when I'd remove AllowOverride All from the <Directory> block in the vhost. So the doc root .htaccess was somehow overriding the rewrite conditions.
I've actually already fixed my initial issue by moving the doc root .htaccess contents into the vhost file and removing the AllowOverride, but I'm more curious as to why this was happening; more specifically how AllowOverride interacts with RewriteCond and RewriteRule.
My hunch is that the .htaccess in my doc root was overriding the www to non-www redirect, but I'm not sure why that one specifically. For example, the http -> https redirect worked without issue (line 352 of H5BP, uncommented out in mine), it seemed to be just that one redirect. I didnt even think that those rules could be overridden since RewriteCond/RewriteRules feel unique to me.
If there are any, what are the rules that determine how an .htaccess can override a rewrite rule?
If there are any, what are the rules that determine how an .htaccess
can override a rewrite rule?
Conditions and rules don't dictate how .htaccess works. AllowOverride is what allows .htaccess usage. If you have AllowOverride All then .htaccess is allowed, if you have AllowOverride None, then it's not and it will be ignored. In 2.4 None is the default.
.htaccess is per directory so it will take precedence if it's located in a directory that has rules applied as long as .htaccess file usage is allowed there. Which is confgiured in the server config in VirtualHost or Directory directives.
Also using an include for .htaccess in a vhost is a very bad configuration. If you have access to the vhost file or the config, you should create another config and include it with the .htaccess contents.
You should not be using .htaccess files at all actually with access to the server config. See this apache recommendation on not using .htaccess.
https://httpd.apache.org/docs/2.4/howto/htaccess.html#when

Why is the RewriteBase not working?

Here is what I am trying to do:
domain is thinkingmonkey.me
domain has 127.0.0.1 as IP addr
mod_alias is installed.
I have a conf file called directories.conf. In which I have all the configuration pertaining to directories. directories.conf is included in httpd.conf
My directories.conf has
Alias /runs /xhprof/xhprof_html
<Directory /mysite/xhprof/xhprof_html>
Order allow,deny
Allow from all
AllowOverride All
</Directory>
In /mysite/xhprof/xhprof_html/.htaccess. I have the following:
RewriteEngine on
RewriteBase /runs
RewriteRule .* index.php
All I am trying to do is to direct any request under /mysite/xhprof/xhprof_html/ to index.php.
When I request for thinkingmonkey.me/runs with no trailing slashes I get 404 not found.
So, I infer that RewriteBase is not working.
What am I doing wrong?
Alias requires a tailing slash. Nothing to do with RewriteBase.
You could use a rewrite rule to add the slash when needed though. Something like:
RewriteRule ^run$ /run/ [R=301,L]
Put this rule in the htaccess in the root of your server (not in /xhprof/xhprof_html)

mod_rewrite in <Location /> in apache conf causing rewrite in .htaccess file not to work?

I am using zend framework, which has a nifty example .htaccess used for redirecting non-existing locations to index.php to be processed by the framework:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /site/index.php [NC,L]
And here is the apache config for /site
Alias /site "/path/to/zf/project/public"
<Directory "/path/to/zf/project/public">
Options Indexes MultiViews FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
While we are upgrading the site, I want to redirect all traffic to a specific file (offline.html, for example) except for a certain IP (127.0.0.1, for example), so I am trying to use this rule in the apache config:
<Location />
Options Indexes MultiViews FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
RewriteEngine On
RewriteBase /
RewriteCond %{REMOTE_HOST} !^127\.0\.0\.1
RewriteCond %{REQUEST_URI} !/offline\.html$
RewriteRule .* /offline.html [R=302,L]
</Location>
This seems to work, but for some reason it makes my .htaccess file seem not to work. I can access /site just fine, but I can't go any deeper to, for example, /site/controller/action.
Thanks!
The Apache 2.2 and Apache 2.4 documentation of mod_rewrite clearly state that rewrite rules in <Location> directives should be avoided. This caution was not included in the Apache 2.0 documentation.
Although rewrite rules are syntactically permitted in <Location> and <Files> sections (including their regular expression counterparts), this should never be necessary and is unsupported. A likely feature to break in these contexts is relative substitutions.
So strange things can happen. You could remove the <Location> section (and RewriteBase directive) and use these new rewrite rules directly in the <VirtualHost> definition, without any <Directory> or <Location> section. It's even faster.
The only problem with global level rewrite rules is that you do not have the REQUEST_FILENAME already computed (you could hack that a little but here you do not even need REQUEST_FILENAME).
You also have one error in your RewriteRule, you use a Redirect so the rewrite Rule should use a absolute url:
RewriteRule .* http://www.example.com/offline.html [R=302,L]
About the maintenance page, a classic way of handling it is with these two lines:
ErrorDocument 503 /htdocs/err/503.html
RedirectMatch 503 ^/(?!err/)
Where you do not filter on local IP, but the interesting part is that the code used for maintenance is 503 (temporary unavailable) which is more correct (in fact a redirect 307 is even more correct but old browser could have problems with it). To do the same with a local IP restriction and a RewriteRule it would be:
ErrorDocument 503 /offline.html
RewriteCond %{REMOTE_HOST} !^127\.0\.0\.1
RewriteCond %{ENV:REDIRECT_STATUS} !=503
RewriteRule ^ - [L,R=503]
To have these rules in the htaccess file, you'll have to add/remove them by hand when you want to use "offline mode".
A better way to do this through the application is to create a controller plugin.
If the APPLICATION_ENV = 'offline', the plugin would do _forward('offline', 'error', 'default');
Alternatively, you could write the logic in a subclass of Zend_Controller_Action which you use as the base class for your controllers.