Let's Encrypt SSL Setup: Free HTTPS, But Read the Fine Print
I got a panicked message at 2am from a client: "The site is showing a security warning and customers can't check out." The Let's Encrypt certificate had expired. Auto-renewal was "set up." The cron job was there. Certbot was installed. Everything looked correct — except the renewal had been silently failing for weeks because an Nginx config change had broken the ACME challenge path. No alerts, no errors in the obvious places. Just a certificate that quietly expired.
Setting up Let's Encrypt is genuinely easy. Keeping it running reliably is where people get tripped up. This guide covers both.
Why This Matters (Even Though It's "Basic")
Running HTTP in 2026 isn't just a security issue — it's a functionality issue. Modern browsers flag HTTP sites with visible warnings. Chrome DevTools won't let you use certain APIs (geolocation, service workers, camera) without HTTPS. Google's search ranking factors HTTPS. And most importantly, without TLS, every piece of data between your users and your server — passwords, tokens, personal information — travels in plain text across every router between them.
Let's Encrypt removed the last excuse. Free certificates, automated issuance, 90-day validity with automatic renewal. If you're not using HTTPS, it's a choice, not a limitation.
Step 1: Install Certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx -y
certbot --versionGotcha: On newer Ubuntu versions (22.04+), the
certbotsudo snap install --classic certbotStep 2: Get Your Certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.comCertbot does a lot in this one command:
- Verifies you control the domain (via an HTTP challenge or DNS challenge)
- Generates a private key and CSR
- Downloads the signed certificate from Let's Encrypt
- Configures your Nginx server block to use the certificate
- Sets up HTTP-to-HTTPS redirect
- Installs a systemd timer (or cron job) for automatic renewal
That's genuinely impressive for a single command. But "automatically configured Nginx" means Certbot modified your Nginx config, and the changes it makes are sometimes not what you'd write by hand. Always review what it changed:
sudo nginx -t # Test config syntax
sudo diff /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-available/yourdomain.com.bakStep 3: Harden Your SSL Config
Certbot's default config is functional but not optimized. Here's what a hardened config looks like — and if you've read our TLS 1.3 guide, some of this will look familiar:
server {
listen 443 ssl http2;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
# HSTS — force HTTPS for 2 years
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# OCSP Stapling — speeds up certificate verification
ssl_stapling on;
ssl_stapling_verify on;
}A few notes on what these do:
HSTS tells browsers to always use HTTPS for your domain, even if the user types
http://preloadmax-ageOCSP Stapling speeds up the TLS handshake by having your server fetch and cache the certificate's revocation status, rather than making the client check with the certificate authority on every connection. It's a free performance win.
Step 4: Verify Auto-Renewal Actually Works
This is the step most tutorials include as an afterthought. It's the most important one.
# Test renewal without actually renewing
sudo certbot renew --dry-run
# Output: Congratulations, all renewals succeeded.If this succeeds, great. But here's what the dry run doesn't test:
- Nginx reload after renewal: The certificate gets renewed, but Nginx is still serving the old cert from memory until it's reloaded. Certbot usually handles this with a , but verify it's configured:
--deploy-hook
# Check your renewal config
cat /etc/letsencrypt/renewal/yourdomain.com.conf
# Should contain: renew_hook = systemctl reload nginx-
Permissions: If your renewal cron job runs as a different user than the one that originally set up Certbot, it might not have permission to write the new certificates or reload Nginx. I've seen this happen on servers where someone set up Certbot as root but then changed the cron to run as a less privileged user.
-
Port 80 accessibility: Let's Encrypt's HTTP-01 challenge needs to reach port 80 on your server. If a firewall rule changes, or if you're behind a load balancer that doesn't forward port 80, renewals will fail. DNS-01 challenges avoid this issue but require API access to your DNS provider.
My recommendation: Don't just run the dry run once. Set up actual monitoring for certificate expiry. A simple cron job that checks your certificate's expiry date and alerts you if it's less than 14 days away will save you from the 2am panic:
# Add to crontab — checks daily, alerts if cert expires within 14 days
0 8 * * * echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates | grep notAfterTesting Your SSL Grade
After everything is configured:
- SSL Labs: https://ssllabs.com/ssltest/ — aim for A+
- Security Headers: https://securityheaders.com — tests HTTP security headers beyond just TLS
With the hardened config above, you should get an A+ from SSL Labs. If you don't, the report will tell you exactly what to fix.
Wildcard Certificates
If you run multiple subdomains (
app.example.comapi.example.comadmin.example.comsudo certbot certonly --manual --preferred-challenges dns -d "*.yourdomain.com" -d "yourdomain.com"Wildcard certs require DNS-01 validation, which means you'll need to add a TXT record to your DNS. Certbot has plugins for popular DNS providers (Cloudflare, Route53, DigitalOcean) that automate this.
Gotcha: Wildcard certs cover
*.example.comexample.com-d "yourdomain.com"The Realistic Summary
Let's Encrypt is one of the best things that's happened to web security. Free, automated, and widely supported. But "automated" doesn't mean "maintenance-free." Set it up, harden the config, verify auto-renewal works end-to-end (not just the dry run), monitor your certificate expiry, and review your Nginx config after each Certbot renewal to make sure nothing got overwritten. That's 30 minutes of setup for years of trouble-free HTTPS. The 2am phone call is entirely preventable.
Discussion
0 comments
Share your thoughts
No comments yet. Be the first to share your thoughts!