Rewrite request in subdirectory after rewriting into that subdirectory - apache

I have following file structure in my server:
/
source/ # source PHP files (not accessible)
public/ # publicly accessible files (media)
.htaccess # second level htaccess to rewrite path into variable
index.php # request collector
.htaccess # first level htaccess to redirect flow into public dir
In first level htaccess I have rewrite rule moves flow into "public" directory.
RewriteRule ^(?!public/).*$ public/$0 [L,NC]
"public/.htaccess" contains following code:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} !index.php
RewriteRule ^(.*)$ index.php/$1 [QSA,L]
So if requesting existing file it is served. Otherwise request is rewrited like "example.com/namespace/controller/action?var=blah" to "example.com/index.php/namespace/controller/action?var=blah"
And that request should be handled by "public/index.php". However it seems that multiple rewriting is not allowed. Am I right? Or maybe I am making something wrong. I have to admit I can not understand mod_rewrite.

Related

Apache 2.4 .htaccess - Point All Requests to index.html With One Exception

I currently have the following .htaccess:
<IfModule mod_rewrite.c>
Options Indexes FollowSymLinks
RewriteEngine On
RewriteBase /production/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
The web server's document root is currently the /production folder referenced in the above (the .htaccess file is in the parent folder since /production is deleted and rebuilt with every code commit). The above will direct all traffic to my site to index.html.
I would like to make an exception to this. If the request is www.mydomain.com/specialrequest, I would like a PHP script called script.php in the parent folder to run.
To review, this is my /var/www/html directory:
-html
-production
-anotherfolder
-.htaccess
-script.php
Apache is pointing to /var/www/html/production and I would like all requests to go to the index.html file in that directory unless the request is /specialrequest - in which case, I would like script.php to run.
Apache is pointing to /var/www/html/production
So, /var/www/html/production really is defined as the DocumentRoot. And so your .htaccess file is located above the document root. And the file you want to rewrite to (upon receiving this special request) is also above the document root.
Unfortunately, you can't rewrite to a file that is above the document root using .htaccess (regardless of where that .htaccess file is located). This is because the RewriteRule substitution in per-directory .htaccess files takes a URL-path, so trying to rewrite to a URL above the document root is simply invalid. If, however, you had access to the server config (or virtual host) then you could rewrite to a filesystem path (including above the document root) - instead of a URL-path - using this method.
So, script.php would need to be within the document root in order to be able to do this using .htaccess. In which case, you could do something like:
RewriteEngine On
# Exception
RewriteRule ^specialrequest$ /script.php [L]
# Front controller
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
But in this case, script.php is would need to be located at /html/production/script.php (in the document root), not at /html/script.php (above the document root) - as in your current filesystem structure.
Incidentally, the RewriteBase /production/ directive in your .htaccess file is entirely superfluous. You're not using a relative path substitution anyway, but since /production is the document root then /index.html (a document root relative URL-path) will rewrite to /production/index.html anyway.
If, however, you could write this directly in your server config (or virtual host), then you could do what you require. For example:
RewriteEngine On
# Rewrite to filesystem path (in the server config)
RewriteRule ^/specialrequest$ /var/www/html/script.php [L]
# Front controller
RewriteRule ^/index\.html$ - [L]
RewriteCond %{LA-U:REQUEST_FILENAME} !-f
RewriteCond %{LA-U:REQUEST_FILENAME} !-d
RewriteRule ^/. /index.html [L]

Apache - Redirection - Paths

I have a problem with redirect. Right now I have one page on address like:
http://localhost/Stella/Wiki/index.php
Also in the same directory I have my .htaccess file, which should redirect all requests to the index.php.
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/Stella/Wiki/index.php
RewriteRule (.*)$ /Stella/Wiki/index.php?id=$1 [L,QSA]
This redirection works really good, but I want to use it in relative way, like:
RewriteCond %{REQUEST_URI} !^/index.php
RewriteRule (.*)$ /index.php?id=$1 [L,QSA]
Like without absolute path, because I am going to have many subfolders, and I don't want to have long rules like: /xxx/xxx/xxx/xxx/index.php.
Can you help me to solve this problem? I don't know what to do, or if is it even possible?
RewriteCond %{REQUEST_URI} !^/Stella/Wiki/index.php
RewriteRule (.*)$ /Stella/Wiki/index.php?id=$1 [L,QSA]
If the .htaccess is located at /Stella/Wiki/.htaccess then you can write these directives like:
RewriteRule ^index\.php$ - [L]
RewriteRule (.*) index.php?id=$1 [L,QSA]
As you suggested, this now uses relative paths. Note that relative paths don't start with a slash. If you use a slash prefix then that will be root-relative (ie. relative to the document root of the site).
When you use a relative path in per-directory .htaccess files then the directory-prefix (the filesystem path of where the .htaccess file is located) is added back at the end. So, index.php is in the directory where the .htaccess file is located. You can override this with the RewriteBase directive.
However, this a little different to your directives. Instead of a condition that only processes the directive if we are not already requesting the target URL. We have an exception that prevents any further directives being processed if that URL is already being requested.
Note, however, that this directs all requests to index.php (as your original directive does). Including requests for existing files and directories - if that is a concern?

htaccess - Redirect to subfolder without changing browser URL

I've a domain that contains a subfolder with the web app structure. I added a .htaccess on my root domain to point the public folder on my subfolder web app. It works fine, but when I type www.example.com the browser URL changes to www.example.com/subfolder/public, but I would like that it doesn't change.
This is my .htaccess:
RewriteEngine On
RewriteRule ^.*$ subfolder/public [NC,L]
EDIT
This first .htaccess is used to redirect on subfolder/public, where there is an other .htaccess that makes all the works.
Here the code of the second .htaccess located on www.example.com/subfolder/public/:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
Sorry, just realised what is happening. It has nothing to do with the second .htaccess file in the subdirectory, as mentioned in comments.
RewriteRule ^.*$ subfolder/public [NC,L]
Since public is a physical directory on the file system, you need to include a trailing slash when internally rewriting to that directory. Otherwise, mod_dir is going to try to "fix" the URL by appending a slash - that is where the external redirect is coming from. (mod_dir implicitly triggers an external redirect from subfolder/public to subfolder/public/.)
So, try the following instead in your root .htaccess file:
RewriteRule .* subfolder/public/ [L]
The important thing is the trailing slash. The anchors (^ and $) on the RewriteRule pattern are not required, since you are matching everything. And the NC flag is also not required for the same reason.
As always, make sure the browser cache is clear before testing.
UPDATE#1: The single directive above rewrites everything, including static resources, to the directory subfolder/public/ which then relies on the second .htaccess file in the subdirectory to correctly route the request. In order to allow static resources to be rewritten correctly (represented in the HTML as root-relative URL-paths, of the form "/js/myjs.js") then you will need additional directives in order to rewrite these.
For example, to specifically rewrite all .js and .css files to the real location in /subfolder/public/...
# Rewrite static resources
RewriteRule (.+\.(?:js|css))$ subfolder/public/$1 [L]
# Rewrite everything else to the "public" directory
RewriteRule .* subfolder/public/ [L]
UPDATE#2: To make the above more general, and to rewrite any static resource (images, PDFs, .txt, etc...) we can check for the existence of the file before rewriting, something like:
# Rewrite static resources
RewriteCond %{DOCUMENT_ROOT}/subfolder/public/$1 -f
RewriteRule (.+) subfolder/public/$1 [L]
# Rewrite everything else to the "public" directory
RewriteRule .* subfolder/public/ [L]
This will mean that if any .css does not exist it will be passed through to subfolder/public/.

htaccess internal rewrite for URL with query params

I have a php file /var/www/somepath/index.php which handles requests to
http://example.com/somepath/?q=query
I'd like the same file to handle requests to
http://example.com/some/other/path/?q=query
http://example.com/another/path/?q=query
http://example.com/yet/another/path/?q=query
I do not want to copy the php file into multiple locations in the filesystem. I thought using internal rewrite rules would be a better way to do do it. Here is what I put in top-level .htaccess file /var/www/.htaccess:
RewriteRule ^some/other/path /somepath/%{QUERY_STRING} [PT]
But it does not work. I get a 404 for http://example.com/some/other/path/?q=query
I know that mod_rewrite is enabled because I have external rewrite rules (301 and 302 redirects) in the same .htaccess files and those work.
You can place this rule as your very first rule in the site root .htaccess which will handle any URL with ?q= query parameter:
RewriteEngine On
RewriteCond %{QUERY_STRING} (^|&)q= [NC]
RewriteRule ^ somepath/index.php [L]

www I added to HTTP_HOST via htaccess file disappears when redirecting to second htaccess file

I am struggling with an htaccess file. Actually, it is two files.
But first things first, so my questions are:
1) Why does my .htaccess(1) file add the www at the beginning of the HTTP_HOST and the slash at the end of folder REQUEST_URI IF AND ONLY IF the .htaccess(2) file is not there (deleted or renamed)?
2) What is wrong with the RewriteRule and conditions that I wrote in .htaccess(2) to redirect the REQUEST_URI to /publicfolder/REQUEST_URI? Conditions doesn't seem to work and when I surf to domain.com/nonpublicfolder it goes to domain.com/domainfolder/publicfolder/nonpublicfolder.
My website is structured as follows:
/
.htaccess(1)
domainfolder/
.htaccess(2)
publicfolder/
genericfolder/
index.extention
file.extention
nonpublicfolder/
So I have one htaccess file in the root folder ( .htaccess(1) ) where I:
add 'www' at the beginning of the HTTP_HOST;
add '/' at the end of REQUEST_URI if it does not end with a file extension;
redirect domain.com/anyfolder/anyfile.extention to domain.com/domainfolder/anyfolder/anyfile.extention;
like so:
<IfModule mod_rewrite.c>
# System symbolic links are allowed.
Options +FollowSymlinks
# Runtime rewriting engine enabled.
RewriteEngine On
# HTTP_HOST starts with 'www'.
RewriteCond %{HTTP_HOST} ^(?!www\.) [NC]
RewriteRule ^.*$ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,NC]
# Folder requests end with '/'.
RewriteCond %{REQUEST_URI} ![^/]+\.[a-zA-Z0-9]+$ [NC]
RewriteRule [^/]$ %{REQUEST_URI}/ [R=301,NC]
# Files and folders are in the 'domainfolder' folder.
RewriteCond %{REQUEST_URI} !^/?domainfolder/ [NC]
RewriteRule ^.*$ /domainfolder%{REQUEST_URI} [R=301,NC]
</IfModule>
And then I have my .htaccess(2) file - in the domainfolder folder - where I redirect files and folders requests to the publicfolder folder IF AND ONLY IF they are not pointing to the notpublicfolder folder or to the Google Site Verification file:
<IfModule mod_rewrite.c>
# Runtime rewriting engine enabled.
RewriteEngine On
# Public files and folders are in the 'publicfolder' folder.
RewriteCond %{REQUEST_URI} !^/?domainfolder/publicfolder/ [NC]
RewriteCond %{REQUEST_URI} !^/?domainfolder/nonpublicfolder/ [NC]
RewriteCond %{REQUEST_URI} !^/?domainfolder/googlexxx.html$ [NC]
RewriteRule ^/?(.+)$ /publicfolder/$1 [R=301,NC,L]
</IfModule>
Thank you very much for your time and patience.
(1) mod_rewrite does multiple passes and on each by default it will only open the first .htaccess file it finds walking up the folder hierarchy from the requested file. read my Tips for debugging .htaccess rewrite rules for more discussion of this. Yes you can use a Options setting to change this behaviour but this has a performance hit and I would suggest that you avoid doing so.
(2) When using hierachical .htaccess files, mod_rewrite has to associate URI path to the current directory and can get this wrong. The RewriteBase directive tells mod_rewrite what the true association is, so use this.
Rule order is important. If you don't have a local Apache instance where you have root privilege and can enable rewrite logging, you need to build up your access file(s) incrementally rule-by-rule, testing at each step because you only get a work/doesn't work return. Again my tips explains how to do this.