Draft, not yet published
Automating SSL certificate renewal so it never expires on you again
7 July 2026· 5 min read · by Stackbastion
You wake up to messages that your site is “not secure” and nobody can sign in. The cause is almost always the same: your SSL certificate expired, and nothing was set up to renew it. It’s an embarrassing outage because it’s completely preventable, and it always lands on a weekend.
Why this happens
The little padlock in the browser comes from a TLS certificate that proves your domain is really yours. Since 2020, these certificates are short-lived on purpose. A free Let’s Encrypt certificate is valid for 90 days, and the industry is moving toward even shorter lifetimes. The short window is a security feature: if a certificate leaks, it stops working soon anyway.
The short lifetime is only safe if renewal is automatic. Someone following a tutorial once often runs a manual command to get a certificate, sees the padlock, and moves on. Ninety days later, with no reminder and no cron job, it expires. Every visitor now hits a full-page browser warning, and logins over HTTPS break because the browser refuses to connect.
AI-generated apps rarely handle this at all. The generated code assumes something in front of it terminates HTTPS, but nobody set that something up to renew. So it works for three months, then dies.
The fix isn’t to renew more carefully by hand. It’s to make renewal a background process you never think about.
How to fix it
You have two solid options. Caddy, which does this with zero configuration, or certbot, the standard renewal tool if you’re on Nginx or Apache. Caddy is the simpler answer and it’s what we run.
Option A: Caddy (recommended, automatic by default)
Caddy is a web server that obtains and renews certificates for you, with no extra setup. Point a domain at it and it just works. Here’s a complete Caddyfile that serves your app and handles HTTPS end to end:
# Caddyfile
app.example.com {
# Reverse proxy to your app running on port 3000
reverse_proxy localhost:3000
}
That’s the whole thing. On first request for app.example.com, Caddy contacts Let’s Encrypt, proves you control the domain, installs the certificate, and serves HTTPS. It then checks the certificate on a schedule and renews it automatically when it’s about a third of its life from expiring, roughly 30 days out. It also redirects HTTP to HTTPS for you.
Requirements: your domain’s DNS must point at the server, and ports 80 and 443 must be open. That’s it. There’s no renewal cron job to write, because Caddy runs the renewal loop itself as long as the process is up.
Check what Caddy currently holds:
# See loaded certificates and their expiry
caddy list-certificates
If you want to be sure the running server is actually serving a fresh certificate, ask it directly:
# Prints the certificate's validity dates as your server presents them
echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null \
| openssl x509 -noout -dates
notAfter is your expiry date. With Caddy running, that date keeps sliding forward on its own.
Option B: certbot with Nginx or Apache
If you’re not using Caddy, certbot is the standard tool. When you install it through your system package manager, it sets up a systemd timer or cron job that renews automatically. The key is to confirm that timer exists and works, because that’s the part people skip.
# Confirm the auto-renewal timer is active
systemctl list-timers | grep certbot
# Dry-run a renewal to prove it works, without using up rate limits
certbot renew --dry-run
If the dry run succeeds, real renewals will too. certbot only renews certificates within 30 days of expiry, so running it often is safe and does nothing until renewal is actually due.
Then: alert on expiry as a backstop
Automation can still break. A firewall change closes port 80, DNS moves, a disk fills up. So add a cheap safety net: a monitor that warns you if any certificate is within, say, 20 days of expiring. Uptime Kuma has a built-in certificate-expiry check, and a hosted uptime service can do the same. This is your smoke alarm, so a silent renewal failure reaches you two weeks early instead of at the outage.
Or, we do it for you
Automatic certificate renewal with an expiry alarm on top is part of every Stackbastion setup, so “certificate expired” is a class of outage you never see. If you’re not certain your renewal is actually automatic, get a free audit and we’ll check it.
FAQ
How long do Let’s Encrypt certificates last?
90 days. They’re designed to be renewed automatically, and the recommendation is to renew about 30 days before expiry. If anything in your stack requires manual renewal every 90 days, treat that as a bug waiting to happen and automate it.
Does Caddy really need no configuration for HTTPS?
For the common case, yes. As long as your domain points at the server and ports 80 and 443 are reachable, Caddy gets and renews the certificate on its own. You only add configuration for special cases like wildcard certificates or DNS-based validation.
My certificate already expired. How do I recover fast?
Renew it now. With Caddy, restart the service and it will re-obtain a certificate on the next request. With certbot, run certbot renew --force-renewal, then reload your web server. After that, set up the automation and the alarm so it doesn’t recur.
Do I still need a paid SSL certificate?
For almost all apps, no. Let’s Encrypt certificates are free and trusted by every major browser. Paid certificates mainly matter for niche needs like extended-validation branding. For a vibe-coded app that’s just outgrown its platform, free and automatic is the right call.