SSL certificates expire. When they do, browsers show a "Your connection is not private" warning and your site becomes effectively inaccessible to most users. The fix is trivial — renew the certificate — but finding out about the expiry in time requires actually setting up alerts.
Most teams don't, until it happens once. Here are three ways to make sure it doesn't happen to you.
| Method | Setup time | Maintenance | Best for |
|---|---|---|---|
| Cron + curl script | 15 min | Low | Linux servers you control |
| Let's Encrypt / certbot | 30 min | None (auto-renews) | Public-facing web servers |
| Dedicated monitoring service | 2 min | None | Multiple domains, no server access |
A shell script that checks certificate expiry via OpenSSL and emails you when it's within 30 days. Scheduled via cron to run daily.
#!/bin/bash
# ssl-expiry-check.sh
# Run daily to alert when any cert expires within $DAYS_THRESHOLD
DAYS_THRESHOLD=30
ALERT_EMAIL="you@yourcompany.com"
DOMAINS=(
"yoursite.com"
"api.yoursite.com"
"app.yoursite.com"
)
for DOMAIN in "${DOMAINS[@]}"; do
EXPIRY=$(echo | openssl s_client -servername "$DOMAIN" \
-connect "$DOMAIN:443" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null \
| cut -d= -f2)
if [ -z "$EXPIRY" ]; then
echo "WARNING: Could not check $DOMAIN" | \
mail -s "SSL check failed: $DOMAIN" "$ALERT_EMAIL"
continue
fi
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null \
|| date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -le "$DAYS_THRESHOLD" ]; then
echo "$DOMAIN SSL cert expires in $DAYS_LEFT days ($EXPIRY)" | \
mail -s "SSL expiry warning: $DOMAIN ($DAYS_LEFT days)" "$ALERT_EMAIL"
fi
done
Install and schedule:
# Make executable
chmod +x /opt/scripts/ssl-expiry-check.sh
# Run daily at 9am — add to crontab
0 9 * * * /opt/scripts/ssl-expiry-check.sh
# Test it manually first
./ssl-expiry-check.sh
Pros: No external dependencies. Works for any domain you can connect to. You control the alert threshold and delivery.
Cons: Requires a running server. If your server is down, the check doesn't run. Email via mail needs a configured mail relay. The script needs to be maintained as your domain list changes.
Let's Encrypt certificates expire after 90 days, but certbot automatically renews them when they're within 30 days of expiry. Once set up, you should never deal with certificate renewal again.
# Install certbot (Ubuntu/Debian)
sudo apt install certbot python3-certbot-nginx
# Obtain a certificate (nginx)
sudo certbot --nginx -d yoursite.com -d www.yoursite.com
# Certbot installs a systemd timer or cron job automatically.
# Verify it's there:
sudo systemctl list-timers | grep certbot
# or:
sudo crontab -l | grep certbot
Certbot will auto-renew and reload nginx. But there's a catch: the renewal can fail silently. Certbot logs failures but doesn't email you by default unless you configure it:
# Add to /etc/letsencrypt/cli.ini to get renewal failure emails
post-hook = systemctl reload nginx
pre-hook = systemctl stop nginx # if using standalone
# Enable renewal failure notifications (certbot 1.22+)
certbot renew --dry-run # Test first
# Add to certbot config:
# renew-hook = /opt/scripts/notify-renewal.sh
A more reliable approach is to monitor the certificate externally — even if you use Let's Encrypt for renewal, an external checker verifies the certificate is actually valid and will alert you if renewal fails.
Pros: Fully automatic after setup. 90-day certificates with 30-day renewal window means you always have buffer. Industry standard for public-facing sites.
Cons: Requires domain validation (HTTP-01 or DNS-01 challenges). Doesn't work for wildcard certs without DNS challenge. Let's Encrypt rate limits apply. Renewal failures can still happen — especially after server configuration changes.
A dedicated SSL monitoring service checks your certificates from external infrastructure on a schedule, and emails you at configurable intervals before expiry (typically 30, 14, 7, and 1 day). You add a domain, they handle the rest.
No server access required. No scripts to maintain. Works for domains you don't control the server for (third-party APIs, vendor domains you depend on, client sites you manage).
What to look for in an SSL monitoring service:
SSLWatch monitors your domains 24/7 and sends email alerts 30, 14, 7, and 1 day before any certificate expires. Add a domain in under a minute — no server access required. $9/month for up to 50 domains.
Join the SSLWatch waitlist → Check a domain freeUse Let's Encrypt + certbot if you run your own Linux web server, your site is publicly accessible, and you want zero ongoing maintenance. Pair it with an external monitor to catch renewal failures.
Use a cron script if you have specific servers where you can't or won't use Let's Encrypt — internal services, staging environments, or non-standard setups. Add a dead man's switch to make sure the script itself doesn't fail silently.
Use a dedicated monitoring service if you manage multiple domains, don't have server access, or just want email alerts without maintaining any infrastructure. This is the fastest path from "no monitoring" to "I'll know 30 days before expiry."
For production sites with real users, combining approaches is best: auto-renewal to keep the certificate current, and external monitoring to verify the renewal actually worked.