.htaccess mod_rewrite issue - apache

Almost in any project I work on, some issues with .htaccess occur. I usually just find the easiest solution and leave it because I don't have any knowledge or understanding for Apache, servers etc. But this time I thought I would ask you guys.
This is the files and folders in my (simplified) setup:
/modrewrite-test
.htaccess
/config
/inc
/lib
/public_html
.htaccess
/cms
/navigation
index.php
edit.php
/pages
index.php
edit.php
login.php
page.php
The "config", "inc" and "lib" folders are meant to be "hidden" from the root of the website. I try to accomplish this by making a .htaccess-file in the root that redirects the user to "public_html". The .htacess-file contains this:
RewriteEngine On
RewriteRule (.*) public_html/$1
This works perfect. If I type "http://localhost/modrewrite-test/login.php" in my browser, I end up in public_html/login.php which is my intention. So this works fine. The .htaccess-file in "public_html" contains this:
RewriteEngine On
# Root
RewriteRule ^$ page.php [L]
# Login
RewriteRule ^(admin)|(login)\/?$ login.php [L]
# Page (if not a file/directory)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ page.php?url=$1 [L]
The first rewrite just redirects me to public_html/page.php if I try to reach "http://localhost/modrewrite-test/". The next rewrite is just for the convenience of users trying to log in - so if they try to reach "http://localhost/modrewrite-test/admin" or "http://localhost/modrewrite-test/login" they will end up at the login.php-file. The third and last rewrite handles the rest of the requests. If I try to reach "http://localhost/modrewrite-test/bla/bla/bla" it will just redirect me to public_html/page.php (with the 'url' GET-variable set) instead of finding a folder called "la", containing a folder named "bla" and etc.
All of these things work perfect but a minor issues occurs when I for instance try to reach "http://localhost/modrewrite-test/cms/navigation" without a slash at the end of the URL. When I try to reach that page the browser is somehow redirected to "http://localhost/modrewrite-test/public_html/cms/navigation/". The correct page is shown but why does it get redirected and add the "public_html" part in the URL? The desired behavior is that the URL stays intact and that the page public_html/cms/navigation/index.php is shown.
The files and folders in the (simplified) can be found at http://highbars.com/modrewrite-test.zip

I ran into the same problem with "strange" redirects when trying to access existing directory without slash at end. In my case this redirection was done by mod_dir Apache module. To disable redirection I used DirectorySlash directive. Try putting in .htaccess files following string:
DirectorySlash Off

RewriteBase may help. Try this in public_html/.htaccess:
RewriteEngine On
RewriteBase /

Add the following to /modrewrite-test/.htaccess:
RewriteBase /modrewrite-test
Just to be on the safe side, I'd add the same rule also to /modrewrite-test/public_html/.htaccess. I found that having RewriteBase always set prevents a lot of potential problems in the future. This however means that you might need to update the values if you change the URI structure of your site.
Update:
I don't think that this is possible with your current folder structure. I believe that the problem is that existing subdirectories prevent rewrite rules from firing. Note the behavior please - everything works fine while you are working with non-existent files and directories, thanks to these two conditions:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
However if you try to open any index file from an existing subdirectory, you get redirected to .../public_html/.... Since you can properly open /modrewrite-test/cms/navigation/edit.php, I can only assume that the request is being overwritten by some Apache core directive, which adds slashes at end of folder URLs. Notice that everything works fine if you have an ending-slash at each URL (i.e. the Apache core directory does not need to "correct" your URL, thus everything gets rewritten by your own rewrite rules).
Suggested solution (unless anyone can advise better):
Change /modrewrite-test/public_html/.htaccess as follows:
RewriteEngine On
RewriteBase /modrewrite-test
# Page (if not a file/directory)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ page.php?url=$1 [L]
Then Remove all PHP files from subfolders and use the Front Controller pattern, i.e. route all requests through your main page.php file and do not delegate anything down below.
You can then use the Factory pattern to initiate individual UIs (i.e. navigation/edit.php) directly from your main page.php file based on contents of $_GET['url'] (make sure to properly sanitize that).
Update #2:
This other post on StackOverflow advises on project structure used by Zend Framework - it essentially shows the approach which I suggested above. It is a valuable information asset regardless if you use Zend Framework or not.

Related

Browser seems to show query given by .htaccess

There seems to be a problem with the approach I'm using to redirect everything to index.php which is in my root folder public_html. There should be no exceptions This is ofcourse, unless a folder turns RewriteEngine off. For simplicity lets just say I use www.example.com as my website.
My .htaccess looks as follows:
RewriteEngine on
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
As an example, let's say in the browser I enter: www.example.com/test
It redirects internally as intended to: www.example.com/index.php?url=test
The problem:
I would like the browser to simply still display: www.example.com/test
However, the browser now displays: www.example.com/test/?url=test
This messy name only shows up if I have a folder named test inside the root directory. If I delete the folder, it shows:
www.example.com/test
Yet, if I add the folder again, it shows:
www.example.com/test/?url=test
It still redirects properly, but it doesn't look clean anymore.
What I'm curious about is what exactly causes it to behave this way and how it can be solved/prevented.
Please, note that I always want to rewrite the url. So I do not want to make exceptions for folders. It should look and work the same no matter if there is a folder or not.
edit 1: The root folder would be public_html, so example.com (as an example)
edit 2: Changed phrasing of the question, the question is still the same.
edit 3: Changed the formatting of the question.
edit 4: Added that I want to rewrite the url, regardless whether there exist or doesn't exist a folder.
You need to use this in your .htaccess:
RewriteEngine On
RewriteRule ^([^/]*)$ /test/?url=$1 [L]
This code will leave you with your desired URL. Make sure you clear your cache before testing it.
This may need further edits to do exactly what you want, but using RewriteCond you can specify conditions on when to actually rewrite the url.
RewriteEngine On
RewriteBase /
##perform rewrite only if requested file does not exist...
RewriteCond %{REQUEST_FILENAME} !-f
##perform rewrite only if requested directory does not exist...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)$ /test/?url=$1 [L]

Why isn't this rewrite rule catching?

I have a website with a bunch of assets using absolute paths, ie:
<img src=\"/_files/images/shell/logo.jpg\">
My issue is, the url for this site in development is: http://arraybox.net/~hazelwoodconstru/
Unfortunately, this means it is trying to access the files at arraybox.net/_files/images/shell/logo.jpg instead of arraybox.net/~hazelwoodconstru/_files/images/shell/logo.jpg
I'm trying to handle this with an .htaccess RewriteRule.
RewriteRule /_files/(.*) /~hazelwoodconstru/_files/$1 [R,NC,L]
I had it working at some point, but I messed around with it, and now it's broken and I can't figure out why!
EDIT:
I also have the following in this .htaccess which may or may not be interfering:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !404\.shtml|500\.shtml|favicon\.ico|index\.php{1}
RewriteRule (.*) /~hazelwoodconstru/ [QSA,L]
You need to make the rule's matching more strict, as well as removing the leading slash:
RewriteRule ^_files/ /~hazelwoodconstru/%{REQUEST_URI} [L,R]
Remove the ,R flag if you don't want an external redirect.
What seems to be happening is that the absolute file paths just plain old won't use arraybox.net/~hazelwoodconstru/filepath - they go straight to arraybox.net/filepath. Writing the .htaccess on the main directory arraybox.net won't work for me, because there will be multiple sites using this setup, and there is no way to tell which /home/ directory the assets really live in.
The solution I went with:
Instead of using the user directory arraybox.net/~hazelwoodconstru, I set up a subdomain
hazelwood.arraybox.net.
I then edited the vhosts config to change the document root for this subdomain to point to the /home/hazelwoodconstru directory (I also had to change the user).
Additionally I had to modify vhosts.local to change phpopenbasedirprotect to use documentroot. Then I had to add allow from all on the /home/ directory.
It's now working on the subdomain.

Redirect all to index.php using htaccess

I am writing a simple PHP-based MVC-ish framework. I want this framework to be able to be installed in any directory.
My PHP script grabs the request uri and breaks it off into segments. It makes segment 1 the controller and segment 2 the action. This goes all fine when I do this:
http://www.example.com/mvc/module/test/
It will go to the specific module controller and method. Now I have a default controller, the home controller, which is in folder home.
Now when I access this folder directly http://www.example.com/mvc/home/
It will display a 403 forbidden , because this folder does exist, instead it should also go back to http://www.example.com/mvc/index.php
If I would have installed the framework in a different folder, lets say folder framework it has to redirect back to http://www.example.com/framework/index.php
I would like to redirect every folder and php file back to the index.php, leaving everything else the way it is.
My first problem I encountered was it never redirects to the right folder, always to the domain root folder.
This is what I tried :
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
Your rewrite rule looks almost ok.
First make sure that your .htaccess file is in your document root (the same place as index.php) or it'll only affect the sub-folder it's in (and any sub-folders within that - recursively).
Next make a slight change to your rule so it looks something like:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?path=$1 [NC,L,QSA]
At the moment you're just matching on . which is one instance of any character, you need at least .* to match any number of instances of any character.
The $_GET['path'] variable will contain the fake directory structure, so /mvc/module/test for instance, which you can then use in index.php to determine the Controller and actions you want to perform.
If you want the whole shebang installed in a sub-directory, such as /mvc/ or /framework/ the least complicated way to do it is to change the rewrite rule slightly to take that into account.
RewriteRule ^(.*)$ /mvc/index.php?path=$1 [NC,L,QSA]
And ensure that your index.php is in that folder whilst the .htaccess file is in the document root.
Alternative to $_GET['path'] (updated Feb '18 and Jan '19)
It's not actually necessary (nor even common now) to set the path as a $_GET variable, many frameworks will rely on $_SERVER['REQUEST_URI'] to retrieve the same information - normally to determine which Controller to use - but the principle is exactly the same.
This does simplify the RewriteRule slightly as you don't need to create the path parameter (which means the OP's original RewriteRule will now work):
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L,QSA]
However, the rule about installing in a sub-directory still applies, e.g.
RewriteRule ^.*$ /mvc/index.php [L,QSA]
The flags:
NC = No Case (not case sensitive, not really necessary since there are no characters in the pattern)
L = Last (it'll stop rewriting at after this Rewrite so make sure it's the last thing in your list of rewrites)
QSA = Query String Append, just in case you've got something like ?like=penguins on the end which you want to keep and pass to index.php.
To redirect everything that doesnt exist to index.php , you can also use the FallBackResource directive
FallbackResource /index.php
It works same as the ErrorDocument , when you request a non-existent path or file on the server, the directive silently forwords the request to index.php .
If you want to redirect everything (including existant files or folders ) to index.php , you can use something like the following :
RewriteEngine on
RewriteRule ^((?!index\.php).+)$ /index.php [L]
Note the pattern ^((?!index\.php).+)$ matches any uri except index.php we have excluded the destination path to prevent infinite looping error.
There is one "trick" for this problem that fits all scenarios, a so obvious solution that you will have to try it to believe it actually works... :)
Here it is...
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,QSA]
</IfModule>
Basically, you are asking MOD_REWRITE to forward to index.php the URI request always when a file exists AND always when the requested file doesn't exist!
When investigating the source code of MOD-REWRITE to understand how it works I realized that all its checks always happen after the verification if the referenced file exists or not. Only then the RegEx are processed. Even when your URI points to a folder, Apache will enforce the check for the index files listed in its configuration file.
Based on that simple discovery, turned obvious a simple file validation would be enough for all possible calls, as far as we double-tap the file presence check and route both results to the same end-point, covering 100% of the possibilities.
IMPORTANT: Notice there is no "/" in index.php. By default, MOD_REWRITE will use the folder it is set as "base folder" for the forwarding. The beauty of it is that it doesn't necessarily need to be the "root folder" of the site, allowing this solution work for localhost/ and/or any subfolder you apply it.
Ultimately, some other solutions I tested before (the ones that appeared to be working fine) broke the PHP ability to "require" a file via its relative path, which is a bummer. Be careful.
Some people may say this is an inelegant solution. It may be, actually, but as far as tests, in several scenarios, several servers, several different Apache versions, etc., this solution worked 100% on all cases!
You can use something like this:
RewriteEngine on
RewriteRule ^.+$ /index.php [L]
This will redirect every query to the root directory's index.php. Note that it will also redirect queries for files that exist, such as images, javascript files or style sheets.
Silly answer but if you can't figure out why its not redirecting check that the following is enabled for the web folder ..
AllowOverride All
This will enable you to run htaccess which must be running! (there are alternatives but not on will cause problems https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride)
just in case you were still wondering how to redirect all request either if the directory exists (for core framework folders and files) to the framework index handler, after some error/success attempts just noticed I just needed to change the RewriteCond in the .htaccess file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
the above condition states "not found files" and "not found directories", ok, what if just remove "not found" (!-d) line, and ended with something like the below:
RewriteEngine on
RewriteBase /framework/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /framework/index.php [L,QSA]
It worked for me like a charm
I just had to face the same kind of issue with my Laravel 7 project, in Debian 10 shared hosting. I have to add RewriteBase / to my .htaccess within /public/ directory. So the .htaccess looks a like
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L,QSA]
After doing that don't forget to change your href in,
home
Example:
.htaccess file
RewriteEngine On
RewriteRule ^about/$ /about.php
PHP file:
about

In Apache, how do I redirect from a specific path and query string?

I want to redirect from, e.g.,
http://mystore.com/category.php?id=123
to
http://mystore.com/categories/foo
and also from, e.g.,
http://mystore.com/product.php?id=456
to
http://mystore.com/products/bar
These will be permanent (301) redirects and there will be about a dozen of them. I don't need to extract any information from the paths or query strings, I just need to match them exactly. And I would like avoid specifying absolute URLs if at all possible.
I figure this can be done with mod_rewrite and some combination of RewriteConds and RewriteRules, but I'm already doing some URL rewriting and my attempts so far have had undesired results.
Here's an anonymised excerpt from my .htaccess file before any modifications:
RewriteBase /
RewriteRule sitemap.xml index.php?route=sitemap [L]
# skip files and directories
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^?]*) index.php?route=$1 [L,QSA]
This works as intended. I've tried adding several different combinations of conditions and rules just before the last line, most recently
RewriteCond %{QUERY_STRING} id=123
RewriteRule category.php categories/foo [L,R=301]
Something about that last rule causes problems. The home page loads, but style sheets, images, and other resources do not.
At this point, I'm considering just creating PHP scripts named category.php and product.php to handle the redirects.... Am I just a few characters away from the solution?
The resources (styles sheets, images etc.) are not loaded because there might be relative paths which have become invalid.
The problem is that the browser considers "categories" to be a folder and so the path to the resources is not valid.
A quick fix (if you are running on a domain/subdomain and not in a folder) is to put / in the path of all your resources.
For example: change style.css to /style.css so it is still included when you are on the categories page.
I never did figure out the problem, but I solved it by changing the order of the directives and nothing else. I moved the new redirects to just after the RewriteBase directive and everything works perfectly.

How to do a mod_rewrite redirection to relative URL

I am trying to achieve a basic URL redirection for pretty-URLs, and due to images, CSS etc. also residing in the same path I need to make sure that if the URL is accessed without a trailing slash, it is added automatically.
This works fine if I put the absolute URL like this:
RewriteRule ^myParentDir/([A-Z0-9_-]+)$ http://www.mydomain.com/myParentDir/$1/ [R,nc,L]
But if I change this to a relative URL, so that I don't have to change it each time I move things in folders, this simply doesn't work.
These are what I tried and all do not work, or redirect me to the actual internal directory path of the server like /public_html/... :
RewriteRule ^myParentDir/([A-Z0-9_-]+)$ ./myParentDir/$1/ [R,nc,L]
RewriteRule ^myParentDir/([A-Z0-9_-]+)$ myParentDir/$1/ [R,nc,L]
What is the right way to do a URL redirection so that if the user enters something like:
http://www.mydomain.com/somedir/myVirtualParentDir/myVirtualSubdir
he gets redirected to (via HTTP 301 or 302):
http://www.mydomain.com/somedir/myVirtualParentDir/myVirtualSubdir/
Thanks.
EDIT: Adding some more details because it does not seem to be clear.
Lets say I am implementing a gallery, and I want to have pretty URLs using mod_rewrite.
So, I would like to have URLs as follows:
http://www.mydomain.com/somedir/galleries/cats
which shows thumbnails of cats, while:
http://www.mydomain.com/somedir/galleries/cats/persian
which shows one image from the thumbnails of all cats, named persian.
So in actual fact the physical directory structure and rewriting would be as follows:
http://www.domain.com/somedir/gallery.php?category=cats&image=persian
So what I want to do is put a .htaccess file in /somedir which catches all requests made to /galleries and depending on the virtual subdirectories following it, use them as placeholders in the rewriting, with 2 rewrite rules:
RewriteRule ^galleries/(A-Z0-9_-]+)/$ ./gallery.php?category=$1 [nc]
RewriteRule ^galleries/(A-Z0-9_-]+)/+([A-Z0-9_-]+)$ ./gallery.php?category=$1&image=$2 [nc]
Now the problem is that the gallery script in fact needs some CSS, Javascript and Images, located at http://www.domain.com/somedir/css, http://www.domain.com/somedir/js, and http://www.domain.com/somedir/images respectively.
I don't want to hardcode any absolute URLs, so the CSS, JS and Images will be referred to using relative URLs, (./css, ./js, ./images etc.). So I can do rewriting URLs as follows:
RewriteRule ^galleries/[A-Z0-9_-]+/css/(.*)$ ./css/$1 [nc]
The problem is that since http://www.domain.com/somedir/galleries/cats is a virtual directory, the above only works if the user types:
http://www.domain.com/somedir/gallaries/cats/
If the user omits the trailing slash mod_dir will not add it because in actual fact this directory does not actually exist.
If I put a redirect rewrite with the absolute URL it works:
RewriteRule ^galleries/([A-Z0-9_-]+)$ http://www.mydomain.com/subdir/galleries/$1/ [R,nc,L]
But I don't want to have the URL prefix hardcoded because I want to be able to put this on whatever domain I want in whatever subdir I want, so I tried this:
RewriteRule ^galleries/([A-Z0-9_-]+)$ galleries/$1/ [R,nc,L]
But instead it redirects to:
http://www.mydomain.com/home/myaccount/public_html/subdir/galleries/theRest
which obviously is not what I want.
EDIT: Further clarifications
The solution I am looking for is to avoid hardcoding the domain name or folder paths in .htaccess. I am looking for a solution where if I package the .htaccess with the rest of the scripts and resources, wherever the user unzips it on his web server it works out of the box. All works like that apart from this trailing slash issue.
So any solution which involves hardcoding the parent directory or the webserver's path in .htaccess in any way is not what I am looking for.
Here's a solution straight from the Apache Documentation (under "Trailing Slash Problem"):
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+[^/])$ $1/ [R]
Here's a solution that tests the REQUEST_URI for a trailing slash, then adds it:
RewriteCond %{REQUEST_URI} !(/$|\.)
RewriteRule (.+) http://www.example.com/$1/ [R=301,L]
Here's another solution that allows you to exempt certain REQUEST_URI patterns:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !example.php
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ http://domain.com/$1/ [L,R=301]
Hope these help. :)
This rule should add a trailing slash to any URL which is not a real file/directory (which is, I believe, what you need since Apache usually does the redirect automatically for existing directories).
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+[^/])$ $1/ [L,R=301]
Edit:
In order to prevent Apache from appending the path relative to the document root, you have to use RewriteBase. So, for instance, in the folder meant to be your application's root, you add the following, which overrides the physical path:
RewriteBase /
This might work:
RewriteRule ^myParentDir/[A-Z0-9_-]+$ %{REQUEST_URI}/ [NS,L,R=301]
However, I'm not sure why you think you need this at all. Just make your CSS / JS / image file rewrite rule look something like this:
RewriteRule ^galleries/([A-Za-z0-9_-]+/)*(css|js|images)/(.*)$ ./$2/$3
and everything should work just fine regardless of whether the browser requests /somedir/galleries/css/whatever.css or /somedir/galleries/cats/css/whatever.css or even /somedir/galleries/cats/persian/calico/css/whatever.css.
Ps. One problem with this rule is that it prevents you from having any galleries names "css", "js" or "images". You might want to fix that by naming those virtual directories something like ".css", ".js" and ".images", or using some other naming scheme that doesn't conflict with valid gallery names.
I'm not sure I complelty understand your problem.
The trailing slash redirection is done automatically on most Apache installation because of mod_dir module (99% of chance you'have the mod_dir module).
You may need to add:
DirectorySlash On
But it's the default value.
So. If you access foo/bar and bar is not a file in foo directory but a subdirectory then mod_dir performs the redirection to foo/bar/.
The only thing I known that could break this is the Option Multiviews which is maybe trying to fin a bar.php, bar.php, bar.a-mime-extension-knwon-by-apache in the directory. So you could try to add:
Option -Multiviews
And remove all rewriteRules. If you do not get this default Apache behavior you'll maybe have to look at mod-rewrite, but it's like using a nuclear bomb to kill a spider. Nuclear bombs may get quite touchy to use well.
EDIT:
For the trailing slash problem with mod-rewrite you can check this documentation howto, stating this should work:
RewriteEngine on
RewriteBase /myParentDir/
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+[^/])$ $1/ [R]