Nginx no-www to www and www to no-www
Actually you don't even need a rewrite.
server {
#listen 80 is default
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
#listen 80 is default
server_name example.com;
## here goes the rest of your conf...
}
As my answer is getting more and more up votes but the above as well. You should never use a rewrite
in this context. Why? Because nginx has to process and start a search. If you use return
(which should be available in any nginx version) it directly stops execution. This is preferred in any context.
Redirect both, non-SSL and SSL to their non-www counterpart:
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate path/to/cert;
ssl_certificate_key path/to/key;
return 301 $scheme://example.com$request_uri;
}
server {
listen 80;
listen 443 ssl;
server_name example.com;
ssl_certificate path/to/cert;
ssl_certificate_key path/to/key;
# rest goes here...
}
The $scheme
variable will only contain http
if your server is only listening on port 80 (default) and the listen option does not contain the ssl
keyword. Not using the variable will not gain you any performance.
Note that you need even more server blocks if you use HSTS, because the HSTS headers should not be sent over non-encrypted connections. Hence, you need unencrypted server blocks with redirects and encrypted server blocks with redirects and HSTS headers.
Redirect everything to SSL (personal config on UNIX with IPv4, IPv6, SPDY, ...):
#
# Redirect all www to non-www
#
server {
server_name www.example.com;
ssl_certificate ssl/example.com/crt;
ssl_certificate_key ssl/example.com/key;
listen *:80;
listen *:443 ssl spdy;
listen [::]:80 ipv6only=on;
listen [::]:443 ssl spdy ipv6only=on;
return 301 https://example.com$request_uri;
}
#
# Redirect all non-encrypted to encrypted
#
server {
server_name example.com;
listen *:80;
listen [::]:80;
return 301 https://example.com$request_uri;
}
#
# There we go!
#
server {
server_name example.com;
ssl_certificate ssl/example.com/crt;
ssl_certificate_key ssl/example.com/key;
listen *:443 ssl spdy;
listen [::]:443 ssl spdy;
# rest goes here...
}
I guess you can imagine other compounds with this pattern now by yourself.
More of my configs? Go here and here.
Best Practice: separate
server
w/ hardcodedserver_name
Best practice with nginx is to use a separate server
for a redirect like this (not shared with the server
of your main configuration), to hardcode everything, and not use regular expressions at all.
It may also be necessary to hardcode the domains if you're using HTTPS, because you have to know upfront which certificates you'll be providing.
server {
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
server_name www.example.org;
return 301 $scheme://example.org$request_uri;
}
server {
server_name example.com example.org;
# real configuration goes here
}
Using Regular Expressions within
server_name
If you have a number of sites, and don't care for the most ultimate performance, but want every single one of them to have the same policy in regards to the www.
prefix, then you can use regular expressions. The best practice of using a separate server
would still stand.
Note that this solution gets tricky if you use https, as you must then have a single certificate to cover all of your domain names if you want this to work properly.
non-www
to www
w/ regex in a dedicated single server
for all sites:
server {
server_name ~^(?!www\.)(?<domain>.+)$;
return 301 $scheme://www.$domain$request_uri;
}
www
to non-www
w/ regex in a dedicated single server
for all sites:
server {
server_name ~^www\.(?<domain>.+)$;
return 301 $scheme://$domain$request_uri;
}
www
to non-www
w/ regex in a dedicated server
for some sites only:
It may be necessary to restrict the regex to cover only a couple of domains, then you can use something like this to only match www.example.org
, www.example.com
and www.subdomain.example.net
:
server {
server_name ~^www\.(?<domain>(?:example\.org|example\.com|subdomain\.example\.net))$;
return 301 $scheme://$domain$request_uri;
}
Testing Regular Expressions w/ nginx
You can test that the regex works as expected with pcretest
on your system, which is the exact same pcre
library that your nginx will be using for regular expressions:
% pcretest
PCRE version 8.35 2014-04-04
re> #^www\.(?<domain>(?:example\.org|example\.com|subdomain\.example\.net))$#
data> test
No match
data> www.example.org
0: www.example.org
1: example.org
data> www.test.example.org
No match
data> www.example.com
0: www.example.com
1: example.com
data> www.subdomain.example.net
0: www.subdomain.example.net
1: subdomain.example.net
data> subdomain.example.net
No match
data> www.subdomain.example.net.
No match
data>
Note that you don't have to worry about trailing dots or case, as nginx already takes care of it, as per nginx server name regex when "Host" header has a trailing dot.
Sprinkle
if
within existingserver
/ HTTPS:
This final solution is generally not considered to be the best practice, however, it still works and does the job.
In fact, if you're using HTTPS, then this final solution may end up easier to maintain, as you wouldn't have to copy-paste a whole bunch of ssl directives between the different server
definitions, and could instead place the snippets only into the needed servers, making it easier to debug and maintain your sites.
non-www
to www
:
if ($host ~ ^(?!www\.)(?<domain>.+)$) {
return 301 $scheme://www.$domain$request_uri;
}
www
to non-www
:
if ($host ~ ^www\.(?<domain>.+)$) {
return 301 $scheme://$domain$request_uri;
}
hardcoding a single preferred domain
If you want a little bit more performance, as well as consistency between multiple domains a single server
may use, it might still make sense to explicitly hardcode a single preferred domain:
if ($host != "example.com") {
return 301 $scheme://example.com$request_uri;
}
References:
- http://nginx.org/r/server_name
- http://nginx.org/r/return
- http://nginx.org/en/docs/http/server_names.html
HTTP Solution
From the documentation, "the right way is to define a separate server for example.org
":
server {
listen 80;
server_name example.com;
return 301 http://www.example.com$request_uri;
}
server {
listen 80;
server_name www.example.com;
...
}
HTTPS Solution
For those who want a solution including https://
...
server {
listen 80;
server_name www.domain.example;
# $scheme will get the http protocol
# and 301 is best practice for tablet, phone, desktop and seo
return 301 $scheme://domain.example$request_uri;
}
server {
listen 80;
server_name domain.example;
# here goes the rest of your config file
# example
location / {
rewrite ^/cp/login?$ /cp/login.php last;
# etc etc...
}
}
Note: I have not originally included https://
in my solution since we use loadbalancers and our https:// server is a high-traffic SSL payment server: we do not mix https:// and http://.
To check the Nginx version, use nginx -v
.
Strip www from URL with Nginx redirect
server {
server_name www.domain.example;
rewrite ^(.*) http://domain.example$1 permanent;
}
server {
server_name domain.example;
#The rest of your configuration goes here#
}
So you need to have TWO server codes.
Add the www to the URL with Nginx redirect
If what you need is the opposite, to redirect from domain.example
to www.domain.example
, you can use this:
server {
server_name domain.example;
rewrite ^(.*) http://www.domain.example$1 permanent;
}
server {
server_name www.domain.example;
#The rest of your configuration goes here#
}
As you can imagine, this is just the opposite and works the same way the first example. This way, you don't get SEO marks down, as it is complete perm redirect and move. The no WWW is forced and the directory shown!
Some of my code shown below for a better view:
server {
server_name www.google.com;
rewrite ^(.*) http://google.com$1 permanent;
}
server {
listen 80;
server_name google.com;
index index.php index.html;
####
# now pull the site from one directory #
root /var/www/www.google.com/web;
# done #
location = /favicon.ico {
log_not_found off;
access_log off;
}
}