HTTPS for Self-Hosting: Let's Encrypt and Certbot Done Right
If you self-host anything reachable from the internet, HTTPS is not optional anymore. Browsers warn on plain HTTP, mobile apps refuse insecure connections by default, and anything typed into a plain-HTTP form is visible to anyone on the path. The good news is that free, automated, trusted certificates are a solved problem, and they have been since Let's Encrypt launched as a public Certificate Authority in 2016. Every self-hoster should understand the pieces, because once you do, never having HTTPS again is purely a choice.
The pieces
TLS certificates verify that a server is who it claims to be. Without one, a client has no way to know it is talking to your server rather than an impostor. Certificate Authorities (CAs) issue these certificates after verifying that you control the domain.
Let's Encrypt is a free, automated, open Certificate Authority, run by the non-profit Internet Security Research Group (ISRG). It issues Domain Validated (DV) certificates. The automation is the whole point: certificates from Let's Encrypt are deliberately short-lived, valid for 90 days, which forces renewal to be automated rather than a twice-a-year panic.
ACME is the protocol the automation speaks. A client proves it controls a domain, gets a certificate, and renews it later, all without human intervention.
Certbot is the official ACME client that does the proving and installing for you, and it has plugins for the common web servers, including nginx and Apache.
Issue a certificate
The cleanest path is certbot's nginx plugin, which both obtains the certificate and edits your nginx configuration to serve it, all in one command:
sudo certbot --nginx
You will be asked which domain the certificate should cover, and then asked whether to redirect HTTP to HTTPS. Saying yes to the redirect is almost always the right call; it turns the whole site over to TLS with one answer.
If you would rather make the changes to your nginx config by hand, certbot will still obtain and install the certificate without touching your server blocks:
sudo certbot certonly --nginx
Two environment prerequisites make this smooth. Your site should already be reachable over plain HTTP on port 80, since the default validation method (HTTP-01) requires the CA to reach your server on that port to prove you control the domain. And port 443, where HTTPS lives, needs to be open through your firewall. If you cannot expose port 80, certbot supports validation through a DNS record instead, which also happens to be the path for wildcard certificates.
The part that matters most: automatic renewal
Short certificates are only workable if renewal is fully automated, so this is the step to get right. Modern certbot installs a systemd timer (or, with the snap package, its own built-in timer) that runs twice a day and renews any certificate within 30 days of expiry. You should not live in constant fear of running a renewal command by hand.
Two habits keep this from becoming a 3am outage:
Verify the timer actually exists. On a systemd distro:
systemctl list-timers | grep certbot
If you do not see a certbot timer, your install may be relying on an older cron-based renewal instead. Either mechanism can work, but you want to know which one is carrying you.
Test renewal without touching live certificates. The dry-run exercises the entire renewal path without attempting an actual issuance:
sudo certbot renew --dry-run
A green dry-run is your proof that renewal is wired up end to end. Run it after setup and again before any certificate is due, and you will not be surprised. Also, if you hand-edited your nginx config rather than letting the nginx plugin manage it, add a deploy hook so nginx reloads after each successful renewal and picks up the new certificate:
sudo certbot renew --deploy-hook "systemctl reload nginx"
The caveats worth knowing
- Rate limits are real. Let's Encrypt enforces rate limits on issuance, so do not hammer the production API while testing. That is exactly what
--dry-runand the staging environment are for, and a burnt rate limit delays a real certificate. - The plugin is the easy mode, but know it edits your config. The nginx plugin reads and rewrites your server blocks. If you have unusual configs, review what it changes, or use
certonlyand manage config yourself. - Wildcards need DNS. A wildcard certificate covering *.example.com cannot use the HTTP-01 method; it requires DNS validation, ideally with a DNS plugin for your provider so renewal stays hands-off.
- Automation is non-negotiable, and it is tightening. Certificates are already only 90 days, and the industry is pushing toward shorter lifetimes. The entire design assumes renewal runs without you.
Confirm it worked
After issuing, visit your site with the browser lock icon in view, or check the certificate chain in any TLS debugging tool. The 90-day clock has already started, which is fine, because that is precisely what the renewal timer is for. HTTPS on a self-hosted service is a set-and-forget operation once the automation is verified, and verifying it is a single dry-run command.
Key takeaways
- Let's Encrypt issues free, trusted, 90-day certificates via the ACME protocol, and certbot is the official client with nginx and Apache plugins.
sudo certbot --nginxobtains and installs a certificate and typically enables the HTTP to HTTPS redirect in one step.- Renewal is the whole point: certbot installs a systemd timer that renews twice daily within 30 days of expiry, and
sudo certbot renew --dry-runverifies it. - Respect Let's Encrypt rate limits by testing with
--dry-runand the staging environment, and use DNS validation for wildcard certificates.