DEV Community

Alexey
Alexey

Posted on • Updated on

Modern HTTPS configuration

I've tried many ways of setting up HTTPS servers, and I've finally found a favorite method.

Instead of paying for production certificates, it's easy to verify your own certificates via cerbot https://certbot.eff.org/ and LetsEncrypt https://letsencrypt.org/.

The flow below is for Ubuntu, and involves using nginx to serve a file - as opposed to having the file served by your actual backend. I find this solution to be more elegant if you have full access to your server.

Installing cerbot and receiving a certificate

1. Install certbot

For ubuntu 20:

sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Enter fullscreen mode Exit fullscreen mode

Earlier versions:

sudo add-apt-repository ppa:certbot/certbo
sudo apt-get update
sudo apt-get install certbot
Enter fullscreen mode Exit fullscreen mode

2. Run certbot

sudo certbot certonly --manual
Enter fullscreen mode Exit fullscreen mode

This will stop at a prompt; keep it open and follow the next steps.

3. Set up nginx to serve the right data for certbot

# Snap didn't have nginx when I was doing this setup, so:
sudo apt install nginx
sudo ufw allow 'Nginx HTTP'
Enter fullscreen mode Exit fullscreen mode

(with reference to https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/ ):

# By default nginx will serve files from /var/www/html
# Put the cert there by default, or see what works best for your setup:
sudo mkdir /var/www/html/.well-known
sudo mkdir /var/www/html/.well-known/acme-challenge
sudo vim /var/www/html/.well-known/acme-challenge/<filename from certbot>
<copy in certbot data>
sudo chmod a=r /var/www/html/.well-known/acme-challenge/<filename from certbot>

# We don't need to change anything with the above folder structure.
# Alternatively, we can change the config
sudo vim /etc/nginx/sites-enabled/default
# If you do change the config, reload nginx
sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

4. Finalizing verification

Go back to certbot; it should be prompting to hit Enter. Do that, and the verification should complete:

 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/yourdomain.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/yourdomain.com/privkey.pem
   Your certificate will expire on 2021-06-07. To obtain a new or
   tweaked version of this certificate in the future, simply run
   certbot again. To non-interactively renew *all* of your
   certificates, run "certbot renew"
Enter fullscreen mode Exit fullscreen mode

Using the certificate

The newly created certificates will only be available to root https://certbot.eff.org/docs/using.html#where-are-my-certificates

sudo chmod 0755 /etc/letsencrypt/{live,archive}
# In the doc above, this isn't mentioned as necessary, but I couldn't get access to the privkey w/o being explicit
sudo chmod 0755 /etc/letsencrypt/live/yourdomain.com/privkey.pem
Enter fullscreen mode Exit fullscreen mode

Now, you can either choose to use these certificates directly by your service, or let nginx deal with that layer.

Configuring nginx

To make a server available on HTTPS w/o a port suffix, it's necessary to run on port 443. That requires elevated privileges in linux, and it's not a good idea to run Node.js that way - though nginx is perfectly suited just for this.

A good way to set up port-less access is to configure port forwarding via nginx: from 443 to e.g. 8080 - you can connect from nginx to your service directly via HTTP w/o SSL.

I usually create a file in sites-available, then symlink it inside of sites-enabled:

sudo vim /etc/nginx/sites-available/yourdomain.com
Enter fullscreen mode Exit fullscreen mode

Let's say you're running your application server on port 8080 (you can use HTTP with your application, since nginx will handle the HTTPS handshake/encryption):

server {
  listen 443 ssl http2;
  server_name yourdomain.com www.yourdomain.com;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # managed by Certbot

  include ssl_config;
  access_log            /var/log/nginx/yourdomain.access.log;

  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;

    # Fix the "It appears that your reverse proxy set up is broken" error.
    proxy_pass          http://localhost:8080;
    proxy_read_timeout  90;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
  }

}

server {
    if ($host = yourdomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


  listen 80;
  server_name yourdomain.com www.yourdomain.com;
  return 301 https://yourdomain.com$request_uri;
}
Enter fullscreen mode Exit fullscreen mode

Don't forget to replace yourdomain.com with your actual domain.

After the config is in sites-available, it should be symlinked into sites-enabled:

sudo ln -s /etc/nginx/sites-available/your-domain.com  /etc/nginx/sites-enabled/your-domain.com
Enter fullscreen mode Exit fullscreen mode

If you just have 1 site, you could use sudo vim /etc/nginx/sites-available/default - just append the config above to that file.

Once the config is set up,

sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

HTTPS in local development

Usually, you can rely on HTTP in local development - since browsers special-case behavior for http://localhost, and don't require the same degree of security as with remote hosts.

There are some rare situations where HTTPS locally is useful - e.g.
some OAuth providers that you may need to redirect to your site from may disallow http redirect URLS.

In those cases, mkcert can be very handy ( https://github.com/FiloSottile/mkcert ):

# Only do this once for all mkcert projects
brew install mkcert
brew install nss # for Firefox
mkcert -install
# Set up this repo with mkcert certificates
# I personally just keep my mkcert right in the folder of the repo.
# Don't forget to add the directory to .gitignore!
mkdir mkcert
cd mkcert
mkcert localhost
Enter fullscreen mode Exit fullscreen mode

After that you'll need to get your application server to load those certificates and set up HTTPS - I'll leave that out of scope for this article.

Maintenance

The issued certificates are good for 3 months. To get the time remaining, run:

sudo certbot certificates
Enter fullscreen mode Exit fullscreen mode

To re-issue fresh certificates manually, run:

sudo certbot --force-renewal
Enter fullscreen mode Exit fullscreen mode

This will renew all certificates from certbot (i.e. it's intended to support multiple certificates and services on one machine).

Certbot is built to be automated - so choose your own style, set up a crontab if you like. The general-purpose renew command is

sudo certbot renew
Enter fullscreen mode Exit fullscreen mode

For more information, see https://certbot.eff.org/docs/using.html?highlight=renew#renewing-certificates

Top comments (0)