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
Earlier versions:
sudo add-apt-repository ppa:certbot/certbo
sudo apt-get update
sudo apt-get install certbot
2. Run certbot
sudo certbot certonly --manual
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'
(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
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"
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
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
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;
}
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
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
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
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
To re-issue fresh certificates manually, run:
sudo certbot --force-renewal
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
For more information, see https://certbot.eff.org/docs/using.html?highlight=renew#renewing-certificates
Top comments (0)