invisible mod_rewrite is not always invisible!? ("www" and "without subdomain") - apache

My site is on a host using cPanel 11.
Unfortunatly it redirects both "www.e-motiv.net" and "e-motiv.net" to public_html.
I want resp. public_html/www and public_html/ and this invisible to the end user.
I thought the best way was through mod_rewrite, so I did the following.
File space looks like this (from public_html/):
/.htaccess
/index.php
/www/index.html
/www/test/index.html
And I want this (second part invisible!):
e-motiv.net -> /index.php
www.e-motiv.net -> /www/index.php
www.e-motiv.net/test -> /www/test/index.php
I thought this would do it:
RewriteCond %{HTTP_HOST} ^www.e-motiv.net$
RewriteCond %{REQUEST_URI} !/www
RewriteRule ^(.*)$ /www/$1 [NC,L]
1 and 2 work, but although 3 gives the right file, it changes the address!? (so not invisible)
So, in address bar you get: www.e-motiv.net/test -> www.e-motiv.net/www/test/
Huh??
If mod_rewrite is not the best solution, please do tell!

This is because of mod_dir. mod_dir adds the tailing slashed to urls that map to directories. mod_dir is not aware of these 'virtual urls' created with mod_rewrite.
So either disable this behavior by using
DirectorySlash Off
This will however make requests to www.example.com/folder result in a 404 not found. You can fix this with some rewriterule though. So the complete solution would be something like:
DirectorySlash Off
#www dir only
RewriteCond %{DOCUMENT_ROOT}/$0 -d
RewriteRule ^www/(.+[^/])$ /$1/ [R,L]
#other dirs
RewriteCond %{DOCUMENT_ROOT}/$0 -d
RewriteRule ^(.+[^/])$ /$1/ [R,L]

Related

htaccess rewrite root url to subfolder with accessing other folder?

I have following folder structure in apache server.
Public_html
-->admin
--->admin_login.php
-->website
--->index.php
since the index.php inside the website folder,
i have given following code in .htaccess
RewriteCond %{REQUEST_URI} !^/website/
RewriteRule ^(.*)$ /website/$1 [L,NC]
so that the when the user enter root url , it will appear "www.myurl.com" instead of "www.myurl.com/website/"
but the issue is, i could not be able to access admin_login.php.
is there anyway to modify .htaccess, to come website/index.php in main url and able to access admin_login.php(both)?
Thanks in Advance
You need to add an exception to existing rule like this:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/(website|admin)/ [NC]
RewriteRule .* website/$0 [L]
Negative condition %{REQUEST_URI} !^/(website|admin)/ will match every URI except URIs that start with /website/ or /admin/. This will allow you to directly open www.myurl.com/admin/admin_login.php.
With your shown samples and attempts please try following .htaccess rules file. Please make sure to clear your browser cache before testing your URLs.
RewriteEngine ON
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^index\.php/?$ website/index.php [QSA,NC,L]

htaccess pretty urls not working

Folder structure:
- assets
- all css / js
- calsses
- all models, db ant etc
- views
- admin
- app
- index.php
- customers.php
.......
my .htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTP_HOST} ^(www.)?localhost:8080$
RewriteRule ^(.*)$ /views/$1
RewriteRule ^(/)?$ /views/index.php [L]
address : localhost:8080/app/ - working fine, but then I try to add pretty url for example in my customers.php - localhost:8080/app/customers.php?id=5 change to localhost:8080/app/customers/id/5
htaccess added new line:
RewriteRule /id/(.*) customers.php?id=$1
It's not working, it always return 500 Internal Server Error there could be the problem?
plus Need all urls without .php extend
You'd have to include those conditions for every rule. You'd be better off just rewriting everything to, say views/router.php then using PHP to include the different controllers, or serve a 404 when the URL isn't valid.
RewriteRule !^views/router\.php$ views/router.php [NS,L,DPI]
I agree with Walf in that handling routes through a router class is a better idea (especially in the long run!) than using .htaccess redirects.
However, as your question seems to be more about why is this not working than about how you should do it, here is an explanation for what is going on.
I will be using these URLs as examples:
localhost:8080
localhost:8080/app
localhost:8080/app/customers/id/5
Your first rule:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTP_HOST} ^(www.)?localhost:8080$
RewriteRule ^(.*)$ /views/$1
As you intended, this RewriteRule will match any URL which is not a file, not a directory, and made to localhost:8080.
localhost:8080 # not matched because it leads to a directory.
localhost:8080/app -> localhost:8080/views/app
localhost:8080/app/customers/id/5 -> localhost:8080/views/app/customers/id/5
Your next rule:
RewriteRule ^(/)?$ /views/index.php [L]
It is important to realize that RewriteCond statements apply only to the first RewriteRule following them, thus all that is being checked here is the path.
Side note: ^(/)?$, as you are not using $1, can be simplified to ^/?$.
localhost:8080 -> localhost:8080/views/index.php
localhost:8080/views/app # not matched
localhost:8080/views/app/customers/id/5 # not matched
As the L flag is specified, Apache will immediately stop the current iteration and start matching again from the top. The documentation is badly worded. Thus, localhost:8080/views/index.php will be run through the first rule, fail to match, be run through this rule, fail to match, and then as no other rules exist to check (yet) no rewrite will be done.
Now lets look at what happens when you add your broken rule.
RewriteRule /id/(.*) customers.php?id=$1
There are a few problems here. First, as you don't require that the URL start with /id/ the rule will always match a URL that contains /id/, even if you have already rewritten the URL. If you amended this by using ^/id/(.*), then you would still have issues as the string that the rewrite RegEx is tested against has leading slashes removed. Lastly and most importantly, customers.php does not exist in your root directory.
localhost:8080/views/index.php # not matched
localhost:8080/views/app # not matched
localhost:8080/views/app/customers/id/5 -> localhost:8080/customers.php?id=5
This is the last rule in your file currently, so now Apache will start over. customers.php does not exist in your directory, so it will be rewritten to views/customers.php. No other rules matched, but the URL has changed and so Apache will start over again, as /views/customers.php does not exist, it will be rewritten to /views/views/customers.php ... This pattern will repeat until you hit the maximum iteration limit and Apache responds with a 500 error.
You can solve this several ways. Here would be my preferred method, but only if you cannot use a router.
RewriteEngine on
# Rewrite the main page, even though it is a directory
RewriteRule ^/?$ views/index.php [END]
# Don't rewrite any existing files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .? - [S=999,END]
RewriteRule ^app/?$ views/app/index.php [END]
RewriteRule ^app/id/(.*)$ views/app/customers.php?id=$1 [END]
TL;DR Use a PHP based router. .htaccess rules can be incredibly confusing.
Please refer to the question, How to make Clean URLs
I think this is what you needed.
you can use RewriteRule ^(.*)$ index.php [QSA,L]
Having another crack.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{HTTP_HOST} !^(?:www\.)?localhost:8080$ [OR]
RewriteCond $0 =views
RewriteRule [^/]* - [END]
RewriteRule ^(app|admin)/([^/]+) views/$1/$2.php [DPI,END]
RewriteRule ^(app|admin)/?$ views/$1/index.php [DPI,END]
You may have to use L instead of END flags if your Apache is older. Set up an ErrorDocument for 404s, too.
Don't muck around with query strings, just parse $_SERVER['REQUEST_URI'] in PHP, e.g. start by exploding it on /. Then you'll have all the parameters of the original pretty URL. You can do that part in an include so each controller can reuse the same code.
I tried your structure and .htaccess file myself and found an endless loop in the apache logs. I bet you got something like this:
Mon Nov 28 19:57:32.527765 2016] [core:error] [pid 10] [client 172.18.0.1:35048] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.
I could fix it by adding the last rule like:
RewriteRule id/(.*) /views/app/customers.php?id=$1
The leading / is not needed for the match and the target needs the full path. Note that I got the id double (e.g. 123/123) on the url: http://localhost:8080/id/123.
This is caused by one of the 2 previous rules (removing them fixes it) so you might need to change them.
Here is what you want :
RewriteEngine On
RewriteBase /app/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^\/?$ views/index.php [L]
RewriteRule ^([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)\/?$ views/$1.php?$2=$3 [L]
RewriteRule ^([a-zA-Z0-9]+)\/?$ views/$1.php [L]

htaccess rewrite real directory path to a different, non-existing url, hiding the real path

I would like to use a directory ___test like /___test/subdirectory1/ and have some contents, but never show this path in the url. Instead I would like the url /demo/subdirectory1 or say, /demo/subdirectory1/images/image.jpg to point to /___test/subdirectory1/images/image.jpg and the url on the browser to stay like: /demo/subdirectory1/images/image.jpg. Actually replacing ___test with demo
What I have tried with various problems although it looks like it works sometimes is:
RewriteRule ^demo/subdirectory1(.*) ___test/subdirectory1/$1
The above works only on: demo/subdirectory1 or demo/subdirectory1/ (with existing content example an index.html inside /___test/subdirectory1/) but not on demo/subdirectory1/somethingmore... although the content is there in this case as well, unfortunately it shows the real directory path in the url.
Additionally, I am using the following to through 404 to anything starting from /___test in the url:
RewriteCond %{THE_REQUEST} ^GET\ /___test/
RewriteRule ^___test/(.*) - [R=404,L,NC]
It works, but when I add this, then the previous goes to 404 too, unfortunately (again).
But most important for me is to make the first part right, even if I never make the second work.
Any help really appreciated.
Thanks.
Edit 1:
I have also tried adding the following inside an /___test/subdirectory1/.htaccess
Options +FollowSymLinks
RewriteEngine On
RewriteBase /___test/subdirectory1
no success.
Edit 2:
Although it doesn't work well, the best I came up with so far with the help of Jon Lin is the following:
RewriteCond %{REQUEST_URI} ^/demo/subdirectory1(.*)
RewriteCond %{DOCUMENT_ROOT}/___test/subdirectory1/%1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/___test/subdirectory1/%1 -d
RewriteRule ^ /___test/subdirectory1/%1 [L]
/demo/subdirectory1 <- OK
/demo/subdirectory1/ <- OK
/demo/subdirectory1/subdirectory2 <- Exposes real path
/demo/subdirectory1/subdirectory2/ <- OK
/demo/subdirectory1/subdirectory2/subdirectory3 <- Exposes real path
/demo/subdirectory1/subdirectory2/subdirectory3/ <- OK
As you can see, whatever is deeper level than subdirectory1 has problem. I just cannot understand why. Testing this on a clean .htaccess file existing in the root of the site (no other deeper) on a linux apache.
For the most part you're on the right track, but you may need to add a couple of checks so that the __test directory isn't exposed:
RewriteEngine On
# prevent mod_dir from redirecting to trailing slash
RewriteCond %{REQUEST_URI} ^/demo/subdirectory1/(.*[^/])
RewriteCond %{DOCUMENT_ROOT}/___test/subdirectory1/%1 -d
RewriteRule ^ /demo/subdirectory1/%1/ [L,R=301]
RewriteCond %{REQUEST_URI} ^/demo/subdirectory1$
RewriteRule ^ /demo/subdirectory1/ [L,R=301]
# check if the file actually exists first
RewriteCond %{REQUEST_URI} ^/demo/subdirectory1/(.*)
RewriteCond %{DOCUMENT_ROOT}/___test/subdirectory1/%1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/___test/subdirectory1/%1 -d
# and rewrite only if the resource exists within ___test
RewriteRule ^ /___test/subdirectory1/%1 [L]
Making sure that you've ended rewriting in the current iteration by using [L].
Your 404 rule should work but you should remove the trailing slash so that mod_dir won't try to redirect and expose the ___test directory:
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /___test
RewriteRule ^ - [R=404,L,NC]

URL Rewriting using .htaccess

To change the URL /mobiles.php?id=5 to /mobiles/5
The content of .htaccess file is as follows:
Options +FollowSymlinks
RewriteEngine on
RewriteRule /mobiles/$1 ^/mobiles.php?id=([0-9]+)$
But still it is showing /mobiles.php?id=5 in the address bar. Please help. Is there anything else needs to be added in the .htaccess file?
Note:
mod_rewrite module is enabled
I have restarted Apache server after making changes to the .htaccess
file
.htaccess file is in htdocs folder of Apache.
I am using Windows + PHP + Apache + MySQL
This works for me:
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^mobiles/([0-9]+)$ mobiles.php?id=$1&rew [L]
RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^mobiles.php$ /mobiles/%1? [R,L]
If you see this line:
RewriteRule ^mobiles/([0-9]+)$ mobiles.php?id=$1&rew [L]
I have added rew variable in the query string to prevent Apache to fall in an infinite loop
When Apache execute this line:
RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
Is to make sure that url has not been rewritten for Apache
If your only concern is that the old url stays in the address bar, and you want this not to happen, try adding an [R] at the end.
RewriteRule ^/mobiles.php?id=([0-9]+)$ /mobiles/$1 [R]
Did you actually see the correct page?
By the way, the rewrite rules generally go the other way. I would be expecting to see something like:
RewriteRule ^/mobiles/([0-9]+)$ /mobiles.php?id=$1
Is your concern one of making sure a URL with query parameters does not show up in the address bar?
If I understand correctly, you want
Internally redirect /mobiles/5 to /mobiles.php?id=5
Also redirect the browser TO /mobiles/5 if a user navigates to /mobiles.php?id=5
For this you need 2 rules one to internally rewrite the URL for 1st case and 2nd for browser redirection.
You can do it like this:
RewriteEngine on
# for internal rewrite
RewriteRule ^/?mobiles/([0-9]+)/?$ /mobiles.php?id=$1 [L]
# for browser redirect
RewriteRule ^/?mobiles\.php\?id=([0-9]+)$ /mobiles/$1/ [R,L]
You are doing the opposite, should be:
RewriteRule ^/something/mobiles/([0-9]+)$ /something/mobiles.php?id=$1

mod_autoindex does not respect mod_rewrite rules

I have a directory structure similar to:
public_html/
example.com/
index.php
subdir/
file.jpg
I'm using shared hosting, so http://example.com maps to /public_html/ for its root, and I can't change this. I've added a mod_rewrite rule to handle this issue:
RewriteEngine On
RewriteRule ^$ example\.com/ [L]
RewriteRule (.*) example\.com/$1/ [L]
If I browse to http://example.com/subdir (without the trailing /) it will list file.jpg , but the URL for it will be http://example.com/file.jpg. The parent directory link is http://example.com/example.com/.
If I browse to http://example.com/subdir/ (with the trailing /) it will list file.jpg with the proper URL: http://example.com/subdir/file.jpg. However, the parent directory link is http://example.com/example.com/subdir/.
I'm very confused for what's going on and I'd love any help on this.
(Note that if I take off the final / in the mod_rewrite rule then going to http://example.com/subdir without the / will redirect to the http://example.com/example.com/subdir/ variant. Also, the parent directory for the listing at http://example.com/subdir/ changes to http://example.com/example.com/, which is almost correct.)
This could be caused by a disabled DirectorySlash. So try to enable it or use this rule to do the same with mod_rewrite:
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .*[^/]$ %{REQUEST_URI}/ [L,R=301]