I\'m attempting to redirect www to non-www for both HTTP and HTTPS requests. My root .htaccess looks like this:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^w
Perhaps you could try the following:
RewriteEngine on
# Check if the host contains "www."
RewriteCond %{HTTP_HOST} ^www\.
# Check if we're using HTTPS
RewriteCond %{HTTPS}s ^on(s)|off
RewriteCond http%1://%{HTTP_HOST} ^(https?://)(www\.)?(.+)$
# Redirect accordingly
RewriteRule ^ %1%3%{REQUEST_URI} [R=301,L]
The first rule is taking precedence over https
request because it simply met the rewrite condition. The first rule basically tells that match the domain and you can have your rewriterule to kick off. Instead add another condition which tells if its not https
request
So try this:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www.example.com$
RewriteCond %{SERVER_PORT} !^443
RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]
RewriteCond %{HTTP_HOST} ^www.example.com$
RewriteCond %{SERVER_PORT} ^443
RewriteRule ^(.*)$ https://example.com/$1 [L,R=301]
You need ssl certificate for https
protocol to work
Also I've added [L]
flag which tells to not process further rules
Inspired by @MikeRockétt's answer, this is a "simplified" version, using just one condition (instead of three). This also canonicalises a FQDN (ie. by removing an optional trailing dot on the hostname):
RewriteEngine On
RewriteCond %{HTTP_HOST}#%{HTTPS}s ^www\.(.+?)\.?#(?:on(s)|)
RewriteRule ^ http%2://%1%{REQUEST_URI} [R=301,L]
This 301 redirects www to non-www while maintaining the same protocol, HTTP or HTTPS. Can be used unaltered in either .htaccess
(directory context) or in the main server config / vhost. (Although, if you are in a virtualhost context you should probably be using a simpler mod_alias Redirect
instead.)
The TestString %{HTTP_HOST}#%{HTTPS}s
(which evaluates to a string of the form "www.example.com#ons") is compared against the CondPattern ^www\.(.+?)\.?#(?:on(s)|)
The condition is successful for any request whose Host
header starts "www.".
^www\.(.+?)\.?
first checks that the Host
starts with "www." (if not, it fails early) and captures the remaining host header (excluding an optional trailing dot) in the %1
backreference. The regex that captures the domain name, ie. (.+?)
is made non-greedy so that it does not capture the optional dot at the end.
#
is just an arbitrary character that is used to separate the two values HTTP_HOST
and HTTPS
. Importantly, #
cannot itself appear in either of these two values.(?:on(s)|)
is a non-capturing group that uses alternation to match against %{HTTPS}s
. It either matches on(s)
and captures the "s" (in the %2
backreference) or it matches anything and captures nothing (so %2
is empty).Providing the previous condition is successful then the substitution string (http%2://%1%{REQUEST_URI}
) is evaluated and the request redirected. The backreference %2
, from the preceding CondPattern, either holds an "s" (to make "https" in the substitution) or is empty (ie. "http") and %1
is the hostname less the "www." prefix.
The REQUEST_URI
server variable holds the full URL-path, including the slash prefix.
must my server have an SSL certificate?
Yes, otherwise the browser will block the connection (complaining about an invalid security certificate) - or simply time-out (because your server is not responding on port 443) and the request will not even reach your server.