.htaccess problems with https redirect - .htaccess

I have just upgraded my website from HTTP to HTTPS and my .htaccess, which worked perfectly before, is now behaving strangely. I have the following in there:
Remove trailing slash
Remove php extension
...plus others, but I guess these are the problematic ones.
The behaviors I'm getting, on an example page, are:
http://www.navanter.com/e-tips - no problem
https://www.navanter.com/e-tips.php - no problem
http://www.navanter.com/e-tips.php - seems to double the URL in the address bar
Am I missing something obvious? Code is:
Order Deny,Allow
DirectoryIndex index.php
# Secure text files
<Files ~ "\.txt$">
Order allow,deny
Deny from all
</Files>
# Redirect non-www to www:
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
ErrorDocument 404 /404.php
RewriteEngine On
# All calls go to SSL
RewriteEngine On
RewriteCond %{ENV:HTTPS} !=on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
# Unless directory, remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/$ https://www.navanter.com/$1 [R=301,L]
# Redirect external .php requests to extensionless url
RewriteCond %{THE_REQUEST} ^(.+)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.+)\.php$ https://www.navanter.com/$1 [R=301,L]
# Resolve .php file for extensionless php urls
RewriteRule ^([^/.]+)$ $1.php [L]

For this specific problem, your rewrite rules for removing the trailing / and the .php extension shouldn't need the full URL in the substitution. You might also consider reordering some of those rules.
Updated Example:
Require all granted
DirectoryIndex index.php
# Secure text files
<Files ~ "\.txt$">
Require all denied
</Files>
ErrorDocument 404 /404.php
RewriteEngine On
# Redirect HTTP to HTTPS and non-www to www
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://www.%1%{REQUEST_URI} [R=301,L]
# Unless directory, remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ /$1 [R=301,L]
# Redirect external .php requests to extensionless url
RewriteCond %{THE_REQUEST} /([^.]+)\.php [NC]
RewriteRule ^ /%1 [NC,R=301,L]
# Resolve .php file for extensionless php urls
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+)$ /$1.php [NC,L]
A couple things to note here:
The directives above were modified to use the updated apache 2.4 syntax
The RewriteEngine on directive should come before the other rewrite rules
The HTTP -> HTTPS and non-www -> www redirect logic can be done in a single operation
The PHP extension stripping, and subsequent remapping, should be done without the full URL to avoid the duplication behavior you observed. This last bit should fix the problem.
If you need further info on some of the syntax employed here, you can check the mod_rewrite documentation.

Related

Redirect only root index.php to root in .htaccess, not index.php from subdirectories

I need to make some SEO adjustments on a 10 years old code base so I have to proceed with caution.
The task is to do a 301 redirect of https://www.example.com/index.php to https://www.example.com.
The current rules are these:
DirectoryIndex index.php
RewriteEngine On
RewriteBase /
# Force WWW
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]
# Force HTTPS
RewriteCond %{HTTPS} !on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]
# Add a trailing slash to non-files
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule [^/]$ %{REQUEST_URI}/ [L,R=301,NE]
# General rules
RewriteRule ^product/(.*)-(.*)/(.*)-(.*)/$ product.php?category_permalink=$1&category_id=$2&product_permalink=$3&product_id=$4 [QSA,L]
RewriteRule ^categories/(.*)-(.*)/page-(.*)/$ categories.php?permalink=$1&category_id=$2&page=$3 [QSA,L]
RewriteRule ^categories/(.*)-(.*)/$ categories.php?permalink=$1&category_id=$2 [QSA,L]
RewriteRule ^categories/$ categories.php [QSA,L]
RewriteRule ^search/(.*)$ search.php?q=$1 [QSA,L]
I tried adding the following rules and it works fine, but it messes up https://www.example.com/admin/ too and the website admin can't login anymore because of that.
RewriteCond %{THE_REQUEST} ^.*/index\.php
RewriteRule ^(.*)index.php$ /$1 [R=301,L]
So what I'm trying to figure out is: how can I do the redirect only for the index.php file in the root, not for other subdirectories?
Is this possible?
RewriteCond %{THE_REQUEST} ^.*/index\.php
RewriteRule ^(.*)index.php$ /$1 [R=301,L]
The .* subpattern matches any URL-path that precedes index.php, so you basically need to remove that.
However, since you don't appear to be using a front-controller pattern (ie. rewriting the request to /index.php) you don't need the preceding condition.
The following would suffice (placed immediately after the RewriteBase directive and before your existing rules - in order to minimise redirects):
RewriteRule ^index\.php$ https://www.example.com/ [R=301,L]
You will need to clear your browser cache before testing. Preferably test with 302 (temporary) redirects to avoid potential caching issues.
If you wish to make this rule generic and avoid hardcoding the hostname (like your other rules) then you can change it like so:
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+?)\.?$ [NC]
RewriteRule ^index\.php$ https://www.%1/ [R=301,L]
The %1 backreference contains the hostname, less the www. subdomain (if any), as captured in the preceding condition.
You can "simplify" the rule and omit the absolute URL (making it root-relative). For example:
RewriteRule ^index\.php$ / [R=301,L]
However, this will result in two redirects if requesting the non-canonical hostname or protocol (ie. non-www or HTTP). Not that that should necessarily cause a serious issue, particularly if non-canonical requests are rare.

Avoid multiple redirects in contao

I have analyzed my website with the portal web.dev from Google. This told me that I have too many redirects if I call the URL without www and with http:
It goes first to https and without www, then to http with www and only then to https and www.
This is my htacces file:
<IfModule mod_headers.c>
# Allow access from all domains for webfonts (see contao/core-bundle#528)
<FilesMatch "\.(ttf|ttc|otf|eot|woff2?|font\.css)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
<files serviceaccount.json>
Order allow,deny
Deny from all
</files>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
RewriteCond %{HTTP:Authorization} .
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^app\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
RewriteRule ^ %{ENV:BASE}/app.php [L]
</IfModule>
I don't see at all where these redirects are implemented. I also found this variant to redirect directly. Even if I put them in the htaccess file, the redirects remain as described above.
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} ^(.*)$ [NC]
RewriteRule (.*) https://www.%1/$1 [R=301,L]
</IfModule>
Can you tell me how all these redirects come about and why the last code snippet doesn't work?
Some of those redirects might be done by your Hoster. Also, in newer Contao versions, Contao automatically redirects to https in the front end, if HTTPS is enabled in the respective website root's settings in the back end.
In any case, you should implement the redirects yourself, e.g. in your web/.htaccess. For example:
# Redirect to www subdomain
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Redirect to HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Note: the www redirect would redirect any domain that does not start with www. to www.. If you have a multidomain setup with different subdomains, you will need to adjust the www. redirect accordingly.

Redirect 404 error with public_html subfolder as a root folder for website

I used to run a Wordpress website, but started building my own in php. I was building it on a subdirectory on my server and now want to launch the new website. The structure is as follows:
public_html/newversion/index.php
public_html/newversion/category.php
public_html/newversion/category/information.php
I've edited the .htaccess file in public_html to force non-www and https, set newversion as the new document root, and some code to remove the .php extension from URLs (see the .htaccess code below).
That means that I want newversion/index.php to show up when someone goes to example.com, instead of the person needing to go to example.com/newversion/index.php.
Everything works fine, except when I try to access category.php or information.php. Then the browser is automatically redirected to example.com/newversion/category.php and I get a 404 error. When I try to access information.php, the browser tries to open example.com/category/information.php, but I also get a 404 error.
What am I doing wrong?
(FYI, Wordpress is not installed anymore)
# Apache Rewrite Rules
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
# Base Redirects #
# Remove trailing slash from non-filepath urls
RewriteCond %{REQUEST_URI} /(.+)/$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ https://example.com/%1 [R=301,L]
# Include trailing slash on directory
RewriteCond %{REQUEST_URI} !(.+)/$
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+)$ https://example.com/$1/ [R=301,L]
# Force HTTPS and remove WWW
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [OR,NC]
RewriteCond %{https} off
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
# Change root folder to subfolder of public_html
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/newversion%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}/newversion%{REQUEST_URI} -d [OR]
RewriteCond %{DOCUMENT_ROOT}/newversion%{REQUEST_URI} -l
RewriteRule ^ /newversion%{REQUEST_URI} [L]
RewriteEngine On
# Redirect external .php requests to extensionless url
RewriteCond %{THE_REQUEST} ^(.+)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.+)\.php$ http://%{HTTP_HOST}/$1 [R=301,L]
# Resolve .php file for extensionless php urls
RewriteRule ^([^/.]+)$ $1.php [L]
# Set php version
AddHandler application/x-httpd-recommended-php .php .php5 .php4 .php3
</IfModule>
# Restrict access
<Files .htaccess>
order allow,deny
deny from all
</Files>
Took me a long time figuring this out, but I now know what people mean with .htaccess Rewrite voodoo... So, for anyone still needing an answer:
In the end, the order in which you declare htaccess rules and the subdirectory in which you do it matters a lot. Don't ask me how it works exactly, because I don't fully understand. However, this code worked for me.
The underlying code achieves the following:
website root in subdirectory
force non-www and https
remove .php extension from url
open subdirectories as example.com/foo/ whether or not entered with trailing slash
open files in subdirectories as example.com/foo/bar whether or not entered with a trailing slash
.htaccess file in public_html:
# htaccess for public_html
Options -Indexes +FollowSymlinks -MultiViews
ErrorDocument 404 /404.php
# Remove www and force https
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.example.com$ [NC]
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
# add a trailing slash if public/$1 is a directory
RewriteCond %{DOCUMENT_ROOT}/subdirectory/$1 -d
RewriteRule ^(.*?[^/])$ %{REQUEST_URI}/ [L,R=301]
#Prevent direct access to PHP Scripts
RewriteCond %{THE_REQUEST} \.php[?/\s] [NC]
RewriteRule ^ - [R=404,L]
RewriteRule ^/?$ subdirectory/ [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^((?!subdirectory/).*)$ subdirectory/$1 [L,NC]
Then in the subdirectory add the following htaccess file:
# htaccess for subdirectory
# Remove trailing slashes (keep as first rule!)
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ /$1 [NE,R=301,L]
# Open page without php extension
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^\.]+)$ $1.php [NC,L]

Need help correctly configuring .htaccess for HTTPS

I am trying to configure and write my .htaccess file correctly. There are some things I already tried, but somehow they dont all work out.
I am trying to accomplish the following redirects:
Redirect from HTTP to HTTPS for all folders, including subfolders
Remove ".html" endings so the url shows www.domain.com/photos instead of www.domain.com/photos.html
Remove "index.html" when on home page, so it only shows www.domain.com
Some things work, but when going in a subdirectory like www.domain.com/sub, the page changes from HTTPS to HTTP.
Also, when accessing the page via HTTPS, it doesnt correctly load the font, that is specified in the HTML file
()
See my code below:
<IfModule mod_rewrite.c>
AddType text/x-component .htc
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} !=on
# This checks to make sure the connection is not already HTTPS RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
# This rule will redirect users from their original location, to the same location but using HTTPS. # i.e. http://www.example.com/foo/ to https://www.example.com/foo/ # The leading slash is made optional so that this will work either in httpd.conf or .htaccess context
# remove .html; use THE_REQUEST to prevent infinite loops
RewriteCond %{THE_REQUEST} ^GET\ (.*)\.html\ HTTP
RewriteRule (.*)\.html$ $1 [R=301]
# remove index
RewriteRule (.*)/index$ $1/ [R=301]
# remove slash if not directory
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} /$
RewriteRule (.*)/ $1 [R=301]
# add .html to access file, but don't redirect
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteCond %{REQUEST_URI} !/$
RewriteRule (.*) $1\.html [L]
</IfModule>
I figured it out and it works so far:
<IfModule mod_rewrite.c>
AddType text/x-component .htc
RewriteEngine On
RewriteBase /
# Redirect to www
RewriteCond %{HTTP_HOST} !^www\.domain\.ch [NC,OR]
# Redirect to https
RewriteCond %{HTTP:CF-Visitor} '"scheme":"http"'
RewriteRule ^ https://www.domain.ch%{REQUEST_URI} [NE,R=301,L]
# remove .html; use THE_REQUEST to prevent infinite loops
RewriteCond %{THE_REQUEST} ^GET\ (.*)\.html\ HTTP
RewriteRule (.*)\.html$ $1 [R=301]
# remove index
RewriteRule (.*)/index$ $1/ [R=301]
# remove slash if not directory
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} /$
RewriteRule (.*)/ $1 [R=301]
# add .html to access file, but don't redirect
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteCond %{REQUEST_URI} !/$
RewriteRule (.*) $1\.html [L]
</IfModule>

mysterious exception in htaccess rewrites

My htaccess files contains only a few lines that firstly remove the www and then add ".php" to the slug to get the correct php file, so
www.kalicup.fr/seo
should rewrite to
kalicup.fr/seo
and then display the file seo.php (without the .php extension displaying in the url itself)
at the moment
kalicup.fr/seo
correctly displays seo.php without showing the file extension.
however, when I try
www.kalicup.fr/seo
it rewrites to
kalicup.fr/seo.php
adding the .php extension in the url
so there's abviously a problem in my htaccess but I can't see it !
here's my code
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
ErrorDocument 404 /404.php
# redirect the url with www to url without
RewriteCond %{HTTP_HOST} ^www\.(([a-z0-9_]+\.)?kalicup\.fr)$ [NC]
RewriteRule .? http://%1%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} ^www\.(([a-z0-9_]+\.)?kalicup\.co\.uk)$ [NC]
RewriteRule .? http://%1%{REQUEST_URI} [R=301,L]
# add .php to urls
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*?)/?$ $1.php [L]
can anyone see the problem ?
Use that in your .htaccess:
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
ErrorDocument 404 /404.php
# redirect the url with www to url without
RewriteCond %{HTTP_HOST} ^www\.(([a-z0-9_]+\.)?kalicup\.(?:fr|co\.uk))$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
# add .php to urls
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*?)/?$ $1.php [L]
Only one test for .fr and .co.uk.
And -MultiViews: http://httpd.apache.org/docs/2.2/en/mod/core.html#options
The effect of MultiViews is as follows: if the server receives a request for /some/dir/foo, if /some/dir has MultiViews enabled, and /some/dir/foo does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name. It then chooses the best match to the client's requirements.
http://httpd.apache.org/docs/2.2/en/content-negotiation.html

Resources