How to Secure Your Self-Hosted Apps with Let’s Encrypt and Certbot: HTTPS in 10 Minutes

There’s something deeply satisfying about seeing that little padlock icon in your browser. It means your connection is encrypted, your data is private, and you — not some corporation — are in control of your infrastructure.

If you’re self-hosting anything on the internet (a blog, a dashboard, a personal API), HTTPS isn’t optional anymore. Browsers flag HTTP sites as “not secure,” search engines penalize them, and some APIs simply refuse to connect. The good news? Getting valid TLS certificates used to require money and manual renewal. Now it’s free, automated, and surprisingly simple.

In this tutorial, you’ll set up Let’s Encrypt certificates with Certbot, configure automatic renewal, and harden your HTTPS configuration so your self-hosted apps get an A+ rating on SSL Labs.

What You’ll Need

  • A Linux server (Ubuntu 22.04 or similar) with root or sudo access
  • A domain name pointing to your server’s IP address (an A record)
  • Port 80 and 443 open in your firewall
  • A web server (Nginx or Apache) already running
  • About 10 minutes of your time

Why Let’s Encrypt?

Let’s Encrypt is a free, automated, and open certificate authority. Since launching in 2015, they’ve issued over 3 billion certificates. Their mission is to make the entire web HTTPS. Here’s why it works so well:

  • Free forever. No credit card, no renewal fees, no upsells.
  • Automated. Certbot handles certificate issuance, installation, and renewal.
  • Trusted. Every browser and operating system trusts Let’s Encrypt.
  • Short-lived. Certificates expire every 90 days, which limits damage from compromised keys.

The short expiration might sound like a downside, but it’s actually a security feature. And with auto-renewal, you’ll never have to think about it.

Step 1: Install Certbot

Certbot is the official client for Let’s Encrypt. It’s packaged in most Linux distributions. On Ubuntu:

sudo apt update
sudo apt install certbot python3-certbot-nginx

If you’re using Apache instead of Nginx, swap the last package:

sudo apt install certbot python3-certbot-apache

Why the python3-certbot-nginx package? It includes a plugin that can automatically configure Nginx to use your certificate. Without it, you’d have to manually edit your server blocks. The plugin saves time and prevents misconfiguration.

Step 2: Verify Your Domain Points to This Server

Before requesting a certificate, make sure your domain actually resolves to this machine’s IP address:

dig +short yourdomain.com

This should return your server’s public IP. If it doesn’t, go to your DNS provider and create an A record pointing yourdomain.com to your server. DNS changes can take a few minutes to propagate, so be patient.

Common pitfall: If you’re behind a NAT or reverse proxy, make sure ports 80 and 443 are forwarded to this server. Let’s Encrypt needs to reach your server on these ports to verify domain ownership.

Step 3: Obtain Your Certificate

Now for the magic. Run Certbot with the Nginx plugin:

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot will:

  1. Connect to Let’s Encrypt’s servers
  2. Verify you control the domain (via an HTTP challenge on port 80)
  3. Generate a certificate and private key
  4. Automatically edit your Nginx config to use the certificate
  5. Set up a redirect from HTTP to HTTPS

You’ll be asked for an email address (for renewal notices) and prompted to agree to the terms. The whole process usually takes under 30 seconds.

If everything works, you’ll see a success message like:

Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/yourdomain.com/fullchain.pem

Step 4: Test Your HTTPS Configuration

Open your browser and navigate to https://yourdomain.com. You should see the padlock icon. Click it to inspect the certificate — it should show your domain, the Let’s Encrypt issuer, and an expiration date 90 days from now.

For a more thorough check, visit SSL Labs’ SSL Test and enter your domain. With Certbot’s default Nginx configuration, you should get an A rating. To push that to A+, you can add a few hardening directives (covered in Step 6).

Step 5: Set Up Auto-Renewal

Here’s where Certbot really shines. Let’s Encrypt certificates expire every 90 days, but Certbot can renew them automatically. Test the renewal process first:

sudo certbot renew --dry-run

This simulates the renewal without actually issuing new certificates. If you see no errors, you’re good to go.

Certbot ships with a systemd timer that checks for expiring certificates twice a day and renews any that expire within 30 days. Verify it’s active:

sudo systemctl status certbot.timer
sudo systemctl list-timers | grep certbot

If the timer isn’t installed (some distributions use a cron job instead), you can create one manually:

sudo systemctl enable --now certbot.timer

Or, if you prefer a cron job:

sudo crontab -e
# Add this line:
0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

The --quiet flag suppresses output (so you don’t get cron emails), and the --post-hook reloads Nginx after a successful renewal so it picks up the new certificate.

Step 6: Harden Your HTTPS Configuration (Optional)

Certbot’s default config gets you an A rating. To reach A+ on SSL Labs, add these directives to your Nginx SSL configuration:

# In your server block or a shared SSL config file
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000" always;

Let’s break down what these do:

  • TLS 1.2 and 1.3 only. Older protocols (SSLv3, TLS 1.0, 1.1) have known vulnerabilities.
  • Modern cipher suites. These are the encryption algorithms your server will offer. The ones listed are secure and widely supported.
  • HSTS header. Tells browsers to always use HTTPS for your domain for the next two years. Once set, browsers will refuse to connect over HTTP.
  • OCSP stapling. Speeds up certificate validation by letting your server cache the certificate status instead of browsers checking each time.

After editing, test your Nginx config and reload:

sudo nginx -t
sudo systemctl reload nginx

Troubleshooting Common Issues

“Connection refused” or “Timeout” during verification

Let’s Encrypt needs to reach your server on port 80. Check:

  • Your firewall: sudo ufw status (allow port 80)
  • Your DNS: dig +short yourdomain.com (should point to this server)
  • Your hosting provider’s security group (if using a VPS)

“Too many certificates already issued” error

Let’s Encrypt has rate limits. You can only request 5 duplicate certificates per week. If you’re testing repeatedly, use the --staging flag to get fake certificates that won’t count against your limit:

sudo certbot --nginx -d yourdomain.com --staging

Once you’re confident it works, remove the staging certificate and request a real one.

Certificate renewed but browser still shows old certificate

Nginx caches SSL sessions. After renewal, reload Nginx:

sudo systemctl reload nginx

Also, check that your Nginx config points to the correct certificate path. Certbot typically uses:

ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

What’s Next?

Congratulations — your self-hosted app is now serving HTTPS with a trusted certificate, and it’ll stay that way automatically. Here are some ideas to take it further:

  • Wildcard certificates: Use certbot certonly --manual --preferred-challenges dns -d '*.yourdomain.com' to get a certificate that covers all subdomains. Requires adding a DNS TXT record.
  • Multiple domains: You can add -d flags for each domain, or use Certbot with config files for complex setups.
  • Monitoring: Set up a simple cron job that checks certificate expiration and alerts you if renewal fails.
  • Reverse proxy setups: If you’re using Nginx as a reverse proxy (like in our previous tutorial), Certbot works the same way — just point it at the server block that handles your domain.

HTTPS used to be a luxury. Now it’s a baseline expectation, and Let’s Encrypt has made it accessible to everyone. There’s no excuse for serving unencrypted traffic in 2026 — and no reason to pay for certificates when they’re free.

Go secure your stuff. That padlock looks good on you.

Leave a Reply

Your email address will not be published. Required fields are marked *