Site accessible with multiple URLs - .htaccess

I'm using the micro framework Silex on my website hosted on a VPS.
So, the site files are in the /site_name/public_html/ folder but, with Silex, the site must point to the /site_name/public_html/web/ folder.
In the public_html directory, I have the following .htaccess file :
Options -Indexes -MultiViews
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Redirect to https & www
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L,NE]
# Redirect incoming URLs to web folder
RewriteCond %{REQUEST_URI} !web/
RewriteRule (.*) /web/$1 [L]
</IfModule>
And, in the /public_html/web/ folder, the following .htaccess :
<IfModule mod_rewrite.c>
# Redirect incoming URLs to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
</IfModule>
Now, everything works fine but my pages are accessible with three different patterns :
example.com/page/ (the one I want to keep)
example.com/web/page/
example.com/web/index.php/page/
I have used the meta canonical to avoid duplicate content but I still want these last two options to not exist.
I guess I have something to change in both .htaccess files but I can't find what it is.

I would actually remove the .htaccess file in the /web subdirectory altogether and rewrite directly to /web/index.php in the root .htaccess file. By having two .htaccess files you are seemingly creating extra work. The mod_rewrite directives in the subdirectory will completely override the parent directives (by default), so your canonical HTTPS and www redirects are also being overridden.
(Presumably you had a RewriteEngine On directive in the /web/.htaccess file?)
Having removed the /web/.htaccess file, try something like the following in your root .htaccess file:
Options -Indexes -MultiViews
RewriteEngine On
RewriteBase /web
# Redirect to https & www
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=302,L,NE]
# If /web or /index.php is present at the start of the requested URL then remove it (via redirect)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(?:web|index\.php)/(.*) /$1 [R=302,L]
# Front-controller...
# Internally rewrite all requests to /web/index.php (uses RewriteBase set above)
RewriteRule index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
The check against the REDIRECT_STATUS environment variable ensures we only test initial requests and not requests that have been later rewritten.
The <IfModule> wrapper is not required, unless your site is intended to work without mod_rewrite.
Note that a request like /web/index.php/page/ would result in two redirects. First to /index.php/page then to /page. Since this is an edge case I would consider a double redirect to be acceptable.
UPDATE: I've removed the "directory" check in the above as this would have prevented the document root (example.com/) from being rewritten to the /web subdirectory. This would have consequently resulted in a 403 if you didn't have a directory index document (eg. index.php) in the document root of your site. (However, requests for example.com/page/ should have still worked OK.)
Test with 302 (temporary) redirects and only change to 301 (permanent) when you are sure it's working OK - to avoid any caching issues in the browser. Be sure to clear the browser cache before testing.

Related

.htaccess rewrite to same alias without infinite redirects

I have...
| .htaccess : (v1)
RewriteEngine on
RewriteRule ^in?$ login.php
So, /in --is-really--> /login.php
This much works great. We all can learn how to do this from: .htaccess redirect with alias url
But, I want it to also work in reverse...
If someone should enter /login.php into the address bar, I want it to change to /in.
So also, /login.php --rewrites-to--> /in
From this Answer to a different Question, I want to be ready for anything, using REQUEST_URI. So, my .htaccess file starts with this...
| .htaccess : (v2)
RewriteEngine on
# Remove index.php, if a user adds it to the address
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/(.+/)?index\.php
RewriteRule (^|/)index\.php(/|$) /%1 [R=301,L]
# "in" --> login.php
RewriteRule ^in?$ login.php
That also works great.
But now, I want to add this rule (my Question here) for /in <--> /login.php both ways, just how / <--> /index.php already works with .htaccess (v2). So, I adopted the settings and added a second rule...
| .htaccess : (v3) —not working!
RewriteEngine on
# Remove index.php, if a user adds it to the address
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/(.+/)?index\.php
RewriteRule (^|/)index\.php(/|$) /%1 [R=301,L]
# "in" --> login.php, and also redirect back to it
RewriteCond %{REQUEST_URI} ^/(.+/)?login\.php
RewriteRule (^|/)login\.php(/|$) /%1in [R=302,L]
RewriteRule ^in?$ login.php
...but then /in and /login.php both cause an infinite redirect loop.
What's the right way to do this, still using REQUEST_URI, and still having both rewrite rules (for index.php and for login.php)?
These Questions did not help:
Rewrite rule to hide folder, doesn't work right without trailing slash
This is not about a trailing slash
Allow multiple IPs to access Wordpress Site Admin via .htaccess
This is not about IP-based access
Htaccess URLs redirects are working for http not all https
This is not about https vs http
Rewrite-rules issues : .htaccess
This is not about cleaning up the GET array in the URL
apache htaccess rewrite with alias
This is not about rewriting the host/domain, thereby preserving the path
rewrite htaccess causes infinite loop?
This is not about www subdomain rewrites
.htaccess rewrite page with alias
This is not about rewriting "pretty" URLs nor about how to use slug settings in WordPress
Htaccess alias or rewrite confusion
This is not about simply having multiple rules with the same destination
htaccess rewrite to include #!
I'm not trying to rewrite #!
Reason of redirect loop is a missing RewriteCond %{ENV:REDIRECT_STATUS} ^$ before first redirect rule that removes index.php. Remember that RewriteCond is applicable to immediate next RewriteRule only.
Suggested .htaccess:
RewriteEngine on
# Remove index.php, if a user adds it to the address
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/(.+/)?index\.php$ [NC]
RewriteRule ^ /%1 [R=301,L]
# "in" --> login.php, and also redirect back to it
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/(.+/)?login\.php$ [NC]
RewriteRule ^ /%1in [R=302,L]
RewriteRule ^in?$ login.php [L,NC]
It won't cause redirect loop because after first rewrite to /login.php, variable REDIRECT_STATUS will become 200 and then the RewriteCond %{ENV:REDIRECT_STATUS} ^$ will stop redirect looping.
Thanks to the help from the user with the correct answer, I found that...
RewriteCond %{ENV:REDIRECT_STATUS} ^$
...doesn't go in .htaccess only once, but every time on the line before...
RewriteCond %{REQUEST_URI} ...

.htaccess satisfy www/ssl rules before redirection

I've looked through tens of articles regarding this but none of them has solved my issue and it continues to give me a headache.
Here's what I want to achieve:
1) I have an second domain name which needs redirecting to the main domain while keeping the rest of the URL,
2) All files should lose .php extension,
3) Force WWW,
4) Force HTTPS.
Now the problem is that my current .htaccess only works this way for the main domain and only if the WWW condition is met. Let me explain.
If I type in domain1.com/page or domain2.com/page, it will open https:// www.domain1/2.com/page.php but it should only be /page without .php.
If I type in www.domain1.com/page, it will open https:// www.domain1.com/page - Expected behaviour,but if I type in www.domain2.com/page, it will open https:// domain1.com/page.php.
How can I make sure that any conbination of HTTP/HTTPS and WWW/non-WWW, domain1.com/domain2.com always redirects to https:// www.domain1.com/page ?
I'm also happy for any php code suggestions that I might put the top of every .php file to automatically redirect to the extensionless version but would really love to understand why my .htaccess isn't working.
Here's the code:
## Main Rules
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
## Redirect Domain2.com to Main
RewriteCond %{HTTP_HOST} ^www\.domain2\.com [NC]
RewriteRule ^(.*)$ https://www.domain1.com/$1 [R,L]
## Remove .php Extensions
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [NC,L]
## Force HTTPS
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
## Force WWW
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTPS}s ^on(s)|
RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
ErrorDocument 404 /404
Thanks a lot!
You have the directives in the wrong order. Importantly, the rule that appends the .php extension via an internal rewrite (which you've labelled "Remove .php Extensions") should go at the end, after the external redirects. Your rewrite is appending .php to the requested URL, ie. page becomes page.php and then you are triggering an external redirect which naturally exposes the underlying filename.
However, your directives will result in multiple redirects and can be simplified. The last redirect, for instance, does not canonicalise the protocol (and replies on previous rules to have done this).
For example:
ErrorDocument 404 /404
## Main Rules
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
## Redirect Domain2.com to Main
RewriteCond %{HTTP_HOST} ^(www\.)?domain2\.com [NC]
RewriteRule (.*) https://www.domain1.com/$1 [R,L]
## Force HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
## Force WWW
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
## Append .php Extensions
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule (.*) $1.php [L]
If you have no intention of implementing HSTS then you should reverse the "Force HTTPS" and "Force WWW" rules in order to avoid an additional redirect when requesting http://domain1.com/page.
Note that your initial redirect from domain2.com to domain1.com is a 302 (temporary) redirect.
UPDATE: Note that MultiViews should also be disabled, if not already. For example:
Options +FollowSymLinks -MultiViews
If MultiViews was enabled, then this would also result in the first redirect exposing the file extension since mod_negotiation would issue an internal subrequest for page.php before mod_rewrite processes the request. However, if MultiViews was enabled, then this would also mean that your final rewrite rule that appends the .php extension was effectively being bypassed (since the 2nd conditon would always fail, unless page.php.php existed.).
With MultiViews disabled then this highlights a potential bug with the final rewrite. In that a request for /page/foo, where /page.php exists as an actual file (as in your example) would result in a 500 internal server error due to a rewrite loop. To correct this, the final rewrite rule that appends the .php extension should be changed to the following:
## Append .php Extensions
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule (.*) $1.php [L]
(There's no need to check that the request is not a directory and is a file, since it can never be both.)

.htaccess redirect non-www to www, but within a subdirectory

I have a wordpress installation in the root of the server. I then have a kohana framework in a sub directory. I want to redirect any urls in the kohana framework from non-www to www.
The url structure for the base of the kohana framework is www.mydomain.com/quotes
I've tried adapting the standard redirect I've used before, but it's not working as expected. It redirects to the www version but it adds index.php at the end (www.mydomain.com/quotes/index.php).
EDIT: I forgot to mention I need this to redirect to https as well.
Below is my .htaccess file:
# Turn on URL rewriting
RewriteEngine On
# Installation directory
RewriteBase /quotes
# Protect hidden files from being viewed
<Files .*>
Order Deny,Allow
Deny From All
</Files>
# Protect application and system files from being viewed
RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [L]
# Allow any files or directories that exist to be displayed directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Rewrite all other URLs to index.php/URL
RewriteRule .* index.php/$0 [PT]
# 301 redirect to dubdubdub
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/quotes/$1 [R=301,L]
How do I get this working so that the url redirects to www.mydomain.com/quotes/ and not www.mydomain.com/quotes/index.php

Hidden redirect of / to subfolder using .htaccess in TYPO3

We're setting up a TYPO3 installation, and if the user calls example.com/ we'd like the server to redirect to /typo/index.php?id=106.
This should happen without a change in the address bar. Every other file access on the server (for example example.com/test.png) should be redirected to example.com/typo/test.png).
This is the .htaccess file in the root directory. As I understand, it will redirect everything which doesn't have /typo in the URL to the subfolder and attach the parameters:
RewriteCond %{REQUEST_URI} !^/typo/
RewriteRule ^(.*)$ typo/$1 [L]
Now, this already seems to work, when I call example.com/index.php?id=106 I'm not getting a 404. Unfortunately TYPO3 seems to have some trouble (or the .htaccess configuration isn't correct), because we get a message saying "No input file specified".
What's also missing is the initial redirect when no path is specified. It should then go to /typo/index.php?id=106.
You may try this in one .htaccess file in root directory:
Options +FollowSymlinks -MultiViews
RewriteEngine On
RewriteBase /
# URL with no path
RewriteCond %{REQUEST_URI} ^/?$ [NC]
RewriteCond %{REQUEST_URI} !index\.php [NC]
RewriteRule .* /typo/index.php?id=106 [NC,L]
# URL with path
RewriteCond %{REQUEST_URI} !^/typo [NC]
RewriteRule ^(.+) /typo/$1 [NC,L]
Maps silently:
http://domain.com/ to
http://domain.com/typo/index.php?id=106
and
http://domain.com/anything
http://domain.com/typo/anything
For permanent redirection, replace [NC,L] with [R=301,NC,L]

.htaccess - Subdomains to folders = subfolders 404

Sorry if the title is confusing.
I have set up a virtual host on my local machine. I've set http://dev to map to my /htdocs/dev folder. With some help from dnsmasq and .htaccess, I've set it up so that it maps subdomains of .dev to folders inside /htdocs/dev. And it all works perfect when I try to access, for example, http://dev/file1.html or http://folder.dev/file2.html. The problem occurs with accessing subfolders. I get a 404 Object not found if I try to access http://folder.dev/subfolder/ or http://folder.dev/subfolder/file3.html.
I guess it can be solved with .htaccess, but I failed to do it although I tried. Here's how my /htdocs/dev/.htaccess looks like:
# Default index file
DirectoryIndex index.php
# Interpret .html files as .php scripts
AddHandler php5-script .php .html
# Redirect subdomains to their respective folders
RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_HOST} !^www\.dev$ [NC]
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.dev$ [NC]
RewriteRule !^([a-z0-9-]+)($|/) /%2%{REQUEST_URI} [PT,L]
I should mention that, if I try to access http://dev/folder/subfolder/file3.html, there are no problems.
How can I set addresses such as http://folder.dev/subfolder/ point to /htdocs/dev/folder/subfolder?
Try changing your rules to:
RewriteCond %{HTTP_HOST} !^www\.dev$ [NC]
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.dev$ [NC]
RewriteCond %{DOCUMENT_ROOT}/%2{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}/%2{REQUEST_URI} -d
RewriteRule ^ /%2%{REQUEST_URI} [PT,L]
First, thanks goes to #JonLin for giving me a hint about this.
I've managed to solve this with this .htaccess file:
# Ordered list of index files, if none exist, show directory listing
DirectoryIndex index.php index.html
# Interpret .html files as .php scripts
AddHandler php5-script .php .html
# Redirect subdirectories to their respective folders
RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteCond %{HTTP_HOST} !^www\.dev$ [NC]
RewriteCond %{HTTP_HOST} ^(www\.)?(.*)\.dev(.*)?$ [NC]
RewriteRule !^%2\.dev%3?/$ http://dev/%2%{REQUEST_URI}/ [P]
What Rewrite part of this does is it takes a URL in form of http://folder.dev/subfolder and points it to /dev/folder/subfolder while preserving the URL. It works with or without www.

Resources