Enable HTTPS with Certbot

2 min read07 November 2026

HTTPS encrypts traffic between the browser and the server and avoids warnings from modern browsers. Let's Encrypt issues free certificates valid for ninety days, which Certbot obtains and renews. The hard part is not the command but DNS and ports: one forgotten record makes validation fail.

Prepare DNS and Nginx

Before Certbot, your-domain.com and www.your-domain.com must resolve to the VPS over A and AAAA, and Nginx must answer on port 80 with the correct server_name. Open the ports:

Bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
curl -I http://your-domain.com/

A wrong AAAA record makes the HTTP-01 challenge fail: Let's Encrypt tries IPv6 first. Fix DNS and wait for the TTL before retrying. If another server block captures the domain, Certbot writes the certificate in the wrong place.

Obtain the certificate

Bash
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

The Nginx plugin changes the block to listen on 443, adds certificate paths and offers the HTTP to HTTPS redirect. Files live in /etc/letsencrypt/live/your-domain.com/. Do not copy them by hand: Certbot manages links and renewals.

Renew and test

Bash
sudo systemctl list-timers certbot.timer
sudo certbot renew --dry-run

The systemd timer replaces old cron scripts. --dry-run tests renewal without consuming quota. After renewal, the plugin reloads Nginx. Check the certificate actually served:

Bash
curl -I https://your-domain.com/
openssl s_client -connect your-domain.com:443 -servername your-domain.com </dev/null 2>/dev/null | openssl x509 -noout -dates

If renewal fails, keep port 80 open: the HTTP-01 challenge needs it. Logs are in /var/log/letsencrypt/letsencrypt.log.

HSTS, carefully

Enable HSTS only when the whole domain and its subdomains work over HTTPS:

Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Start with max-age=300, verify, then increase it. includeSubDomains breaks a subdomain still on HTTP; preload is hard to undo. Test with sudo nginx -t and reload.

Troubleshooting

  • Challenge failed: DNS, wrong AAAA, closed port 80 or a higher-priority server block.
  • Too many requests: Let's Encrypt rate limit; wait before retrying, do not loop.
  • Certificate not yet due for renewal: normal, renewal happens thirty days before expiry.
  • Browser still on HTTP: check the redirect and cache, then test from another network.

Do not change TLS protocols blindly: the configuration generated by Certbot is a good starting point for Nginx.

Advertisement

Was this article helpful?

Read next