To clarify what @StephenOstermiller mentioned in comments, nothing has changed in this regard for quite literally decades. Any question you find on the Stack Exchange network, regarding canonical redirects on Apache, will be just as relevant now it was "back in the day".
There is also no single "correct" solution. Due to the variation of servers/configs, different solutions may be required. And due to the powerful/flexible nature of the directives there can be several ways of doing (exactly) the same thing.
So, if you are using a .htaccess
file in the document root directory and you want to redirect from HTTP to HTTPS and from www to non-www and you are not using a CDN (that often acts as a reverse proxy) then you could do this like the following using mod_rewrite (very similar to the non-www to www redirect you posted):
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule (.*) https://example.com/$1 [R=301,L]
The above 301 redirects all requests where HTTPS
is off
or www.example.com
is requested to https://example.com/
, preserving the URL-path and query string.
This needs to go near the top of the .htaccess
file, before any existing rewrite directives. Order matters.
Note that the $
at the end of the 2nd RewriteCond
pattern (CondPattern) is removed to catch fully-qualified-domain-names (FQDNs) that end in a dot. eg. a request of the form http://www.example.com./foo
(note the dot at end of the hostname).
The RewriteRule
pattern (.*)
captures the requested URL-path, which is referred to using the $1
backreference in the RewriteRule
substitution string.
This will not redirect any other subdomains you might have.
(You need RewriteEngine On
once at the top of the file.)
Modify for any directory and the server config
Due to the way the URL-path is captured in the backreference, the above only works if the .htaccess
file is in the document root directory. It also only works in .htaccess
files (or a directory context), as opposed to in a server or virtualhost context (in the main server config). It will result in a double-slash at the start of the URL-path if used in the main server config.
This is because in .htaccess
, the URL-path that is matched by the RewriteRule
pattern excludes the directory-prefix (which always ends with a slash), so the URL-path that is matched never starts with a slash. For example, given a request for example.com/foo/bar
, the URL-path that is matched in the root .htaccess
file is foo/bar
(no slash prefix). Whereas when used in the main server config, it always matches against the full URL-path, ie. /foo/bar
(slash prefix).
Since we are expecting the $1
backreference to not start with a slash, we can resolve this by making the slash prefix optional (as @StephenOstermiller suggests in comments). For example:
:
RewriteRule ^/?(.*) https://example.com/$1 [R=301,L]
So, this now works in both the server config and a .htaccess
file in the document root.
However, this won't work if the .htaccess
file is located in a subdirectory (as opposed to the document root), since the subdirectory will get removed in the redirect (it's not captured by the RewriteRule
pattern). To resolve this you would need to use the REQUEST_URI
server variable instead - which contains the full URL-path, including the slash prefix. For example:
:
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
This now works everywhere (.htaccess
in any directory and the server config) and avoids the capturing backreference, so is arguably the "better" solution. However, it really makes no difference on most configs.
Note that the slash is removed at the start of the URL-path in the substitution string, since the REQUEST_URI
server variable itself already contains the slash prefix.
In summary, the complete rule then looks like:
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
However, if you are implementing this in the server config then you probably wouldn't do it this way to begin with. It would be more efficient to use a mod_alias Redirect
directive instead (in the appropriate vHost container), which is not possible in .htaccess
.
Avoid repetition of the domain name
You could save repetition of the domain name in the substitution string by capturing the required domain name from the preceding CondPattern. And write it like this instead:
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.(example\.com) [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
The %1
backreference contains example.com
captured in the preceding CondPattern. Otherwise, this is very similar to the above rule, except that it will not canonicalise a mixed case Host
header. If www.ExAmPlE.com
is requested then the request will be redirected to ExAmPlE.com
. Although this is rare and will potentially only affect bot traffic (browsers lowercase the hostname before making the request).
Generic "no domain" solution
To make this entirely generic, without explicitly stating the domain then you could instead write the rule like this:
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+?)\.?$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
However, this doesn't only redirect www.example.com
to example.com
. It will also redirect from any www subdomain. For example, www.foo.example.com
will be redirected foo.example.com
and www.another.example
will be redirected to another.example
. The www subdomain is simply removed in all cases. This may or may not be desirable for your config. It's a potential caveat of having an entirely "generic" solution, although can be adjusted for particular cases (although may be less "generic" as a result).
This "generic" version is similar to the previous. The 3rd condition (RewriteCond
directive) is simply used to capture the domain name, less the optional www.
prefix. The 3rd condition always matches the request, regardless of whether HTTPS
is off
or the requested hostname starts www.
.
ASIDE:
A "modern" website...
What would be a modern global rewrite command for HTTPS websites without cname (www.)
Now, you mention "modern". On a modern website you might be considering to implement HTTP Strict Transport Security (HSTS). In which case you need to change the above... so instead of a single redirect to the canonical protocol and hostname you redirect from HTTP to HTTPS on the same hostname first, before canonicalising the hostname. This (unfortunately) potentially results in two redirects, not one (but only affects first time visitors to the non-canonical protocol and hostname, so the impact is minimal). This is a requirement of HSTS.
So, you would do something like the following instead:
# 1. Redirect HTTP to HTTPS (same host)
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# 2. Redirect www to non-www
RewriteCond %{HTTP_HOST} ^www\.(example\.com) [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
And to make the second rule "generic" (ie. without explicitly stating the domain) then...
# 2. Redirect www to non-www (generic version)
RewriteCond %{HTTP_HOST} ^www\.(.+?)\.?$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
(This isn't a complete HSTS solution, for that see the following related question:
HSTS implementation when using www.TLD)
Side note about the rewrite rule presented in your question
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule (.*) https://www.example.com/$1 [L,R=301]
This redirects everything that is not www.example.com
, which naturally includes any subdomains and any other domains that might resolve to your account. Depending on your site this might be "too much" or it might be perfectly OK. However, to redirect only example.com
to www.example.com
, ie. only non-www to www then you would need to modify the 2nd condition to be a positive match like this:
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^example\.com [NC]
RewriteRule (.*) https://www.example.com/$1 [L,R=301]