Nginx redirect (non-www to www) not working with Certbot Nginx redirect (non-www to www) not working with Certbot nginx nginx

Nginx redirect (non-www to www) not working with Certbot


It seems that server_name when translated to the $host variable selects the first in the list of server_name. Let me know if that works. I can't quite test this currently.

Try swapping server_name to server_name www.example.com example.com; as well as changing return 301 https://$host$request_uri; to return 301 https://$server_name$request_uri;

server {    server_name www.example.com example.com;    return 301 https://$server_name$request_uri;}server {    listen 443 ssl;    # SSL CERT STUFF.    server_name example.com;    return 301 https://www.$server_name$request_uri;}server {    listen 443 ssl;    # SSL CERT STUFF.    server_name www.example.com;    # LOCATION STUFF}


This is not an efficient configuration for Nginx request processing. It's messy, your if condition gets evaluated on every request and I don't see where your non www to www is even meant to happen.

I'd split http and https:

server {    listen 80 default_server;    return 301 https://www.example.com$request_uri;}

Thats all non https traffic taken care of in a single redirect. Now for the https:

server {    listen 443 default_server ssl;    server_name www.example.com;    root # should be outside location blocks ideally    ......}

The default server directive means this server will handle any requests which do not have a matching server configuration. If you don't want that then add example.com after www.example.com, not before it. Any requests ending up here will display the first entry in the client browser bar.

Based on your comments you might need to add a separate block for the other domain to avoid an SSL certificate mismatch. Try this:

server {    listen 443 ssl;    server_name example.com;    ssl_certificate .....;    ssl_certificate_key .....;    return https://www.example.com$request_uri;}


Although the OP has accept one of the answers as the solution, I just want to point out that it may not be the best practice.

The correct way is to use $host instead of $server_name (as per Mitchell Walls' example) or hardcoded www.exmple.com (as per miknik's example). Both results an extra 443 server directive that is not necessary and messy.

server {    listen 80 default_server;    server_name www.example.com example.com;    root /var/www/html;    # define your root directory here    return 301 https://$host$request_uri;}server {    listen 443 ssl;    # SSL CERT STUFF.    #server_name www.example.com;    you don't need to specify again here    # LOCATION STUFF}

There is a difference between $host and $server_name:

  • $host contains "in this order of precedence: host name from the request line, or host name from the 'Host' request header field, or the server name matching a request".
  • $server_name contains the server_name of the virtual host which processed the request, as it was defined in the nginx configuration. If a server contains multiple server_names, only the first one will be present in this variable.