Why Bother with SSL?
Remember when browsers started shaming websites with that big red “Not Secure” warning in the address bar? That was the internet collectively deciding that encryption isn’t optional anymore — it’s the baseline. If your site, your self-hosted app, or your homelab dashboard doesn’t have HTTPS, browsers will warn visitors away, and search engines will quietly bury you.
The good news? SSL certificates used to cost money and require paperwork. Those days are gone. Let’s Encrypt gives you valid, trusted certificates for free, and Certbot automates the entire process — including renewal. Once it’s set up, you literally never think about it again.
Let’s get your server locked down.
What You’ll Need
- An Ubuntu server (20.04, 22.04, or 24.04 all work fine)
- A domain name pointing to your server’s IP address (an A record in your DNS)
- Port 80 open in your firewall (Let’s Encrypt needs this for verification)
- SSH access with sudo privileges
Important: Make sure your DNS is already set up and resolving before you start. You can check with dig +short yourdomain.com — it should return your server’s IP.
Step 1: Install Certbot
Certbot is in Ubuntu’s default repositories, but the version there is often outdated. We’ll use snap to get the latest release — this is the method the Certbot team officially recommends.
# Install snap if you don't have it (Ubuntu 22.04+ usually does)
sudo apt update
sudo apt install snapd -y
# Install Certbot via snap
sudo snap install --classic certbot
# Make sure the certbot command is in your PATH
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
# Verify it's working
certbot --version
You should see something like certbot 2.x.x. If you do, you’re golden.
Step 2: Get Your Certificate
How you get the certificate depends on whether you’re running a web server already.
Option A: You’re running Nginx
Certbot can automatically configure Nginx for you. It’s the easiest path:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot will:
- Verify you control the domain (via an HTTP challenge on port 80)
- Download the certificate
- Modify your Nginx config to use HTTPS automatically
- Set up a redirect from HTTP to HTTPS
It’ll ask for your email (for renewal notices) and whether you agree to the terms. Answer yes to the redirect — you want HTTPS enforced.
Option B: You’re running Apache
Same idea, different flag:
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
Option C: No web server yet (or you want the cert only)
Use the standalone method. This temporarily spins up a small web server to prove domain ownership:
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com
The certificates will be saved to /etc/letsencrypt/live/yourdomain.com/. You’ll need to configure your web server to point to them manually.
Step 3: Verify Everything Works
Once Certbot finishes, test it:
# Visit your site in a browser — you should see the padlock icon
# Or check from the command line:
curl -I https://yourdomain.com
You should see HTTP/2 200 (or whatever your normal response is) over HTTPS. No certificate warnings.
For a deeper check, use the SSL Labs test: https://www.ssllabs.com/ssltest/ — enter your domain and wait a couple minutes. You’re aiming for an A+ rating.
Step 4: Set Up Auto-Renewal
Let’s Encrypt certificates expire every 90 days. That sounds short, but it’s actually a feature — shorter lifetimes mean less damage if a key is compromised. The catch: you need to renew before they expire.
Good news: if you installed Certbot via snap, it sets up a renewal timer automatically. Verify it’s active:
# Check the snap renewal timer
sudo snap services certbot.renew
# Or look at systemd timers
timedatectl list-timers | grep certbot
You should see a timer that runs twice daily. Certbot is smart — it only renews certificates that are within 30 days of expiry, so most of those runs are no-ops.
Do a dry run to make sure renewal works without actually renewing:
sudo certbot renew --dry-run
If you see Congratulations, all renewals succeeded, you’re set. The certificates will renew themselves silently in the background, forever.
Common Pitfalls
“Connection refused” during verification: Let’s Encrypt needs to reach your server on port 80. Check your firewall (sudo ufw status) and make sure port 80 is allowed. If you’re behind a cloud provider (AWS, DigitalOcean, etc.), check their security groups too.
DNS not resolving: If you just set up your domain, wait. DNS propagation can take anywhere from a few minutes to 48 hours (usually under an hour). Use dig yourdomain.com from multiple locations to check.
Rate limits: Let’s Encrypt has rate limits — 50 certificates per domain per week, and 5 failed validations per hour per domain. During testing, use the staging server to avoid hitting limits: add --staging to your certbot command. Just remember to remove it when you’re ready for the real certificate.
Certificate works but browser shows “Not Secure”: You probably have mixed content — your page loads over HTTPS but some resources (images, scripts, CSS) are still being loaded over HTTP. Check your browser’s developer console for mixed content warnings.
Where to Go From Here
Now that you have HTTPS working, here are a few things worth exploring:
- HSTS (HTTP Strict Transport Security): Add a header that tells browsers to always use HTTPS for your domain. Certbot adds this automatically with
--nginxor--apache. - Wildcard certificates: Need SSL for
*.yourdomain.com? Use DNS validation:sudo certbot certonly --manual --preferred-challenges dns -d '*.yourdomain.com'. You’ll need to add a TXT record to your DNS. - Multiple services: If you’re running several self-hosted apps (Nextcloud, Grafana, etc.) behind a reverse proxy, you only need one certificate on the proxy. All the backend services can stay on HTTP internally.
That’s it. You now have free, auto-renewing SSL certificates on your server. The internet is a little safer, and your visitors get that satisfying little padlock icon. Not bad for about ten minutes of work.
Leave a Reply