Free HTTPS certificates with Let's Encrypt
Let's Encrypt is a certificate authority that issues free HTTPS certificates for your domain. On your VPS you can get a certificate with a few commands using Certbot, or with no commands at all using Caddy, and both renew it on their own. There are two requirements: the domain must point to your server's IP address, and ports 80 and 443 must be open from outside.
Why do you need HTTPS?
HTTPS encrypts the traffic between the browser and the server. Third parties on the network can't read the password in a login form or the details of an order. Browsers can show a "not secure" warning on sites without HTTPS. A Let's Encrypt certificate is free, so there is no good reason left to skip HTTPS.
Before you start
- The domain's A record must show your server's IP address. To check, run
dig +short example.com. The output should be your server's IP address. - Ports 80 and 443 must be open, because validation happens on port 80 (HTTP-01). If you use ufw, run
sudo ufw allow 80/tcpandsudo ufw allow 443/tcp. If your server has a separate firewall in its panel, check the rules there too. - If the domain has an AAAA (IPv6) record, it must point to this server too. If it doesn't, delete the record.
- Your web server must be running and know this domain.
Installing and checking with Certbot on Nginx or Apache
Certbot's official instructions recommend the snap package. Make sure snapd is installed on the server (on Ubuntu it usually is), and remove any older Certbot installed through apt.
- Install Certbot:
sudo snap install --classic certbot - Make the command available everywhere:
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot - Get the certificate and have it installed in the web server. Run
sudo certbot --nginxfor Nginx orsudo certbot --apachefor Apache. - Answer the questions: your email address, which domains you want the certificate for, and whether to redirect HTTP to HTTPS.
- Try renewal:
sudo certbot renew --dry-run
If you only want to obtain the certificate and set things up yourself, run sudo certbot certonly --nginx. The Certbot package adds a scheduled job (cron or a systemd timer) on its own, and you can see it in the output of systemctl list-timers. sudo certbot certificates lists your certificates and their expiry dates. Certbot renews a certificate as its lifetime runs low, and you do nothing. Renewal can still fail one day (for example because of a firewall rule), so run --dry-run once in a while.
To check the certificate, first look at the padlock icon in the browser's address bar. To see the expiry date with a command, run:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
The notAfter line in the output is the certificate's expiry date. If you don't use the redirect that Certbot adds, redirect HTTP to HTTPS yourself. On Nginx, add the line return 301 https://$host$request_uri; to the server block that listens on port 80.
With Caddy and no commands
If you use Caddy, you don't need to install anything extra. For a domain in its configuration, Caddy obtains the certificate from Let's Encrypt or ZeroSSL, renews it and redirects HTTP to HTTPS. According to its official documentation, the domain's A and AAAA records must point to the server, ports 80 and 443 must be reachable from outside, and Caddy's data folder must be persistent and writable. If you run Caddy in Docker, mount that folder on a volume. Otherwise the certificates disappear when the container is deleted, and asking for them again can hit a rate limit.
Wildcard certificates
A wildcard certificate (*.example.com) covers all subdomains with one certificate, and it can only be obtained with DNS-01 validation. This validation doesn't use port 80. It asks you to put a TXT record in your domain's DNS. For that, your DNS provider needs an API, and you store the API key on the server. The Let's Encrypt documentation calls storing such keys on a web server risky. If you don't need a wildcard, getting certificates for individual domains with HTTP-01 is simpler and doesn't require storing a DNS key.
Common errors
| Symptom | Likely cause | Fix |
|---|---|---|
| Validation fails, domain not found | The A record points to the wrong IP or hasn't propagated yet | Check the IP with dig +short example.com and wait |
| Timeout | Port 80 is closed | Open 80 in ufw and in the panel firewall, if you have one |
| Validation passes sometimes and fails other times | The AAAA record points to another server | Fix or delete the AAAA record |
| Rate limit error | Too many attempts | Use --dry-run for tests and check the official page for the limits |
| Renewal doesn't run | No scheduled job, or the web server blocks port 80 | sudo certbot renew --dry-run shows the error |
The error message usually says which validation failed and what address Let's Encrypt tried to reach on your server. Read the message as it is. The answer is often right there.
According to the official Let's Encrypt page, there is a limit of 5 certificates per week for the same set of domain names and 5 failed validations per hour for the same domain name. So use --dry-run while you experiment. It tries renewal in the staging environment and doesn't spend your real limit.
On Seturack ready-made apps
Ready-made apps (WordPress, Nextcloud and others) open over HTTPS on an sslip.io address derived from your IP address, so no domain name is needed. When you connect your own domain, Caddy obtains the certificate again, and you install nothing extra. The steps for telling Caddy and the app about the domain are in this help article.
Comments0
No Comments Yet
Be the first to share your thoughts.
Please sign in or sign up to leave a comment.