Redirecting https www domain to non-www domain with NGINX.

February 22, 2019 937 views
Nginx Let's Encrypt Ubuntu 18.04

I have been looking but I am unable to find a solution. I have set up SSL certificates for my domain and the http traffic redirects to https. What I would like is https://www.example.com to redirect to https://example.com.

Any help would be appreciated.

1 Answer

You could achieve that with a configuration similar to below. In this case the final destination is passed to a back-end server.

server {
    listen 80;
    server_name example.com;

    location / {
        return 301 https://example.com/$request_uri;
    }

    location ~ /.well-known/acme-challenge/ {
            default_type "text/plain";
            root /usr/share/nginx/html;

            allow all;
    }   
}

server {
    listen 80;
    server_name www.example.com;

    location / {
        return 301 https://example.com/$request_uri;
    }

    location ~ /.well-known/acme-challenge/ {
            default_type "text/plain";
            root /usr/share/nginx/html;

            allow all;
    }   
}

server {
    listen 443 ssl http2;
    server_name www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
    ssl_ecdh_curve secp384r1;

    ssl_dhparam /etc/nginx/dhparam.pem;

    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    ssl_stapling on;
    ssl_stapling_verify on;

    resolver 1.1.1.1 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;


    location / {
        return 301 https://example.com/$request_uri;
    }       
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
    ssl_ecdh_curve secp384r1;

    ssl_dhparam /etc/nginx/dhparam.pem;

    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    ssl_stapling on;
    ssl_stapling_verify on;

    resolver 1.1.1.1 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;

    location / {
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;
        proxy_set_header        Upgrade $http_upgrade;
        proxy_set_header        Connection 'upgrade';

        proxy_pass http://xxx.xxx.xxx.xxx;
    }       
}
  • Thank you for taking the time Jason.

    Here's what my current config looks like.

    server {
        server_name example.com www.example.com;
    
        #location = /favicon.ico { access_log off; log_not_found off; }
        location /static/ {
            root /home/quizthat/django;
        }
        location / {
            include proxy_params;
            proxy_pass http://unix:/run/gunicorn.sock;
        }
    
    
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    
    }
    server {
        if ($host = www.example.com) {
            #changed by me
            return 301 https://example.com$request_uri;
        } # managed by Certbot
    
    
        if ($host = example.com) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
    
    
        listen 80;
        server_name example.com www.example.com;
        return 404; # managed by Certbot
    
    
    
    
    }
    

    What can I do here?

    • Below is how I would setup the config based on what you have provided. I prefer to seperate each domain and sub-domain consideration into their own server block for more granular control.

      This first block handles all the traffic for the domain on Port:80 (http) and redirects it to the https destination.

      server {
          listen 80;
      
          server_name example.com www.example.com;
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      
          location ~ /.well-known/acme-challenge/ {
              default_type "text/plain";
              root /usr/share/nginx/html;
      
              allow all;
          }
      }
      

      Additionally this conatines the directive that allows for Certbot SSL webroot request/renewals. As you can see in the below directive the webroot is /usr/share/nginx/html - to issue the SSL based on this method the following command is used.
      `

      certbot certonly --agree-tos --webroot -w usr/share/nginx/html -d example.com -d www.example.com
      
      ...
          location ~ /.well-known/acme-challenge/ {
              default_type "text/plain";
              root /usr/share/nginx/html;
      
              allow all;
          }
      ...
      

      The following block redirects the www/https traffic to the non-www/https destination - be sure to have your SSL installed before entering this block.

      server {
          listen 443 ssl https;
      
          server_name www.example.com;
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      
          ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
          include /etc/letsencrypt/options-ssl-nginx.conf;
          ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;  
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      }
      

      This final block handles all the destination traffic / application traffic. - be sure to have your SSL installed before entering this block.

      server {
          listen 443 ssl https;
      
          server_name example.com;
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      
          ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
          include /etc/letsencrypt/options-ssl-nginx.conf;
          ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
      
          location /static/ {
              root /home/quizthat/django;
          }
      
          location / {
              include proxy_params;
              proxy_pass http://unix:/run/gunicorn.sock;
          }       
      }
      

      ... I do place all this into one file, but you can separate them into multiple if you like.
      Please note that you may need to adjust the config further to suit your situation.

      server {
          listen 80;
      
          server_name example.com www.example.com;
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      
          location ~ /.well-known/acme-challenge/ {
              default_type "text/plain";
              root /usr/share/nginx/html;
      
              allow all;
          }
      }
      
      server {
          listen 443 ssl https;
      
          server_name www.example.com;
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      
          ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
          include /etc/letsencrypt/options-ssl-nginx.conf;
          ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;  
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      }
      
      server {
          listen 443 ssl https;
      
          server_name example.com;
      
          location / {
              return 301 https://example.com/$request_uri;
          }
      
          ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
          include /etc/letsencrypt/options-ssl-nginx.conf;
          ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
      
          location /static/ {
              root /home/quizthat/django;
          }
      
          location / {
              include proxy_params;
              proxy_pass http://unix:/run/gunicorn.sock;
          }       
      }
      
Have another answer? Share your knowledge.