| Engineering | Infrastructure | How to add SSL HTTPS to Nginx Server (LetsEncrypt)
How to add SSL HTTPS to Nginx Server (LetsEncrypt)

This is an adapted guide from Digital Ocean's How To Secure Nginx with Let's Encrypt on Ubuntu 20.04

Configure DNS

Ensure you have your domain name pointing to your server's IP address. The DNS A records should be as follows:

A Record @ 1.1.1.1 
A Record www 1.1.1.1 

Ensure Server Name is configure in Nginx

Assuming you've followed this previous guide to setup nginx, in your nginx config file sudo nano /etc/nginx/sites-enabled/myapp, ensure you have server_name myapp.com www.myapp.com;

Install Certbot

sudo apt install certbot python3-certbot-nginx

Configure firewall for SSL

sudo ufw enable
sudo ufw status
sudo ufw allow 'Nginx Full'
sudo ufw allow 'OpenSSH'

Check that these are the only rules allowed by typing sudo ufw status:

Status: active
To                         Action      From
--                         ------      ----
Nginx Full                 ALLOW       Anywhere                  
OpenSSH                    ALLOW       Anywhere                  
Nginx Full (v6)            ALLOW       Anywhere (v6)             
OpenSSH (v6)               ALLOW       Anywhere (v6)    

Obtain a certificate

sudo certbot --nginx -d domain.com -d www.domain.com

Run through the certificate generator

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): admin@domain.com
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for domain.com
http-01 challenge for www.domain.com
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/default
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/default

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/default
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/default

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://domain.com and
https://www.domain.com

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=domain.com
https://www.ssllabs.com/ssltest/analyze.html?d=www.domain.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/domain.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/domain.com/privkey.pem
   Your cert will expire on 2020-09-26. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Verify

  1. Visit your domain using http://domain.com you should be redirected to https://domain.com/
  2. Visit your domain using http://www.domain.com you should be redirected to https://www.domain.com/
  3. Visit SSL Labs https://www.ssllabs.com/ssltest/analyze.html?d=domain.com and a SSL report will be generated. This installation method typically scores:
Certificate: 100
Protocol Support: 100
Key Exchange: 90
Cipher Strength: 90
Overall Score: A

Redirecting http to https and www to non-www

If you want to ensure users only use the non-www version of your site, you can modify your nginx config file sudo nano /etc/nginx/sites-enabled/myapp to add redirects. The result will be https://www.myapp.com, http://www.myapp.com, http://myapp.com will all be redirected to https://myapp.com

In your nginx config file you should have two server blocks, one for port 443 (https) and one for port 80 (http). Modify the server blocks to redirect requests to https://myapp.com

For example, you can add in redirect logic into your server block:

  # Redirect all www to non-www
  if ($host = www.myapp.com) {
    return 301 https://myapp.com$request_uri;
  }

The result for your /etc/nginx/sites-enabled/myapp file will be:

server {
  # Listen for https traffic
  listen [::]:443 ssl ipv6only=on;
  listen 443 ssl;

  server_name myapp.com www.myapp.com;

  root /home/deploy/myapp/current/public;

  passenger_enabled on;
  passenger_app_env production;

  location /cable {
    passenger_app_group_name myapp_websocket;
    passenger_force_max_concurrent_requests_per_process 0;
  }

  location ~ ^/(assets|packs) {
    expires max;
    gzip_static on;
  }

  # Allow uploads up to 100MB in size
  client_max_body_size 100m;

  ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/myapp.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

  # Redirect all www to non-www
  if ($host = www.myapp.com) {
    return 301 https://myapp.com$request_uri;
  }
}


server {
  # Listen for http traffic
  listen 80;
  listen [::]:80;

  server_name myapp.com www.myapp.com;

  # Redirect all http traffic to https
  return 301 https://myapp.com$request_uri;
}

Afterwards, restart nginx sudo service nginx restart and ensure it is active sudo service nginx status. Open a browser and visit https://www.myapp.com, http://www.myapp.com, http://myapp.com, you should be redirected successfully to https://myapp.com.

Debugging

Try stopping / starting / restarting nginx

sudo service nginx stop
sudo service nginx start
sudo service nginx restart
sudo service nginx status

‚óŹ nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Sat 0000-00-00 00:00:00 UTC; 10s ago
       Docs: man:nginx(8)
    Process: 61459 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 61475 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=1/FAILURE)

Tailing error nginx logs sudo tail -f /var/log/nginx/error.log showed:

[emerg] 45851#45851: bind() to [::]:80 failed (98: Address already in use)
[emerg] 45851#45851: bind() to 0.0.0.0:80 failed (98: Address already in use)

Use netstat, if netstat is unavailable, install it using apt install net-tools

Find out which process is running on port 80 sudo netstat -plant | grep 80

Kill the process running on port 80 sudo fuser -k 80/tcp

Start nginx sudo service nginx start