The SSL Certificate Nobody Renewed: An Outage Post-Mortem
SSL Certificate Expiry Is Not Just an "Oops" Moment
When an SSL certificate expires, browsers display a full-screen warning: "Your connection is not private." Most visitors leave immediately. They do not investigate. They do not come back. For your client, this means lost traffic, lost sales, and lost trust -- all because of a certificate that should have renewed automatically.
Let's Encrypt and automated renewal have made SSL management easier, but expiry incidents still happen constantly. Here is why.
A Real Case: A 20,000-Yen Certificate Nobody Renewed
This is a case we ran into ourselves.
The site ran on a rented server that the client contracted and managed themselves. The certificate was not a free one — it was a paid certificate costing about 20,000 yen (roughly 130 USD) per year. And critically, it was not set to auto-renew.
With a free certificate, "just turn on auto-renewal" is close to the default choice. Paid certificates change that calculus. The contract and the payment sit on the client's side, so the process settles into "someone handles it around that time every year." The moment nobody remembers that task, the expiry date starts quietly approaching.
So what actually happened? The site went down first, and only then did the client reach out to us. We did not detect the expiry. The browser threw a warning, visitors could not load the site, the client noticed, and the call came in. We renewed it in a hurry after that.
The real failure here is not that someone forgot to renew. It is that the agency was never in a position to detect it at all.
- The server was under the client's control, so we could not inspect cron jobs or certbot logs
- Because renewal costs money, the decision to automate it was not ours to make
- As a result, we had no way of knowing the expiry date was approaching
You Can Still See the Expiry Date From Outside
This is where one property of TLS becomes useful: the certificate expiry date is readable from outside. You do not need shell access. One HTTPS handshake against the public endpoint is enough.
# You do not need server access to read the expiry date
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -enddate
# Example output:
# notAfter=Apr 15 23:59:59 2026 GMT
"We cannot touch their server, so there is nothing we can do" is not true. External monitoring earns its keep precisely on servers you cannot touch. You may not be able to fix their cron job, but being able to say "this expires in 30 days" ahead of time is the difference between an incident and a non-event.
Why Certificates Expire: Two Different Failure Modes
Expiries split into two categories: certificates that were never on auto-renewal, and certificates where auto-renewal existed but failed. The first kind shows up with paid certificates and client-managed servers, as in the case above. The second kind is a technical failure, and it has more variations.
Certificates That Were Never on Auto-Renewal
- A paid certificate obtained manually — an annual cost means a human has to approve and pay for the renewal. This is the case described above
- The server is administered by the client — the agency cannot configure auto-renewal even if it wants to, and ownership of the task is never settled
- The person who issued it left or changed roles — the renewal task itself was never handed over
- A wildcard certificate requiring DNS validation — automation is harder, so manual operation becomes permanent
What makes this pattern dangerous is that technically nothing is broken. No error appears in any log. What needs monitoring is not process success — it is the expiry date itself.
Why Auto-Renewal Fails More Often Than You Think
Common causes of SSL expiry despite auto-renewal being "configured":
- Server migration without transferring cron jobs -- the renewal script stayed on the old server
- DNS changes breaking domain validation -- the ACME challenge can no longer verify domain ownership
- Outdated certbot version -- the renewal process silently errors out
- Manually-obtained certificates -- nobody set up auto-renewal in the first place
- Disk full, certbot log write fails -- certbot exits silently with no trace in logs
For agencies managing many client sites across different hosting environments, catching a single failed renewal among dozens of domains is nearly impossible without automated monitoring.
Check SSL Expiry Dates Manually
Knowing how to check manually is a useful baseline skill.
# Check SSL certificate expiry dates
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates
# Example output:
# notBefore=Jan 15 00:00:00 2026 GMT
# notAfter=Apr 15 23:59:59 2026 GMT
This works for one domain. It does not scale to 50. That is where automated monitoring comes in.
Automate SSL Monitoring With Miterl
Miterl monitors SSL certificate expiry through a dedicated ssl monitor type. When a certificate is approaching expiry, Miterl sends alerts to the alert contacts you have attached -- Slack, Chatwork, LINE, or email.
Create an SSL monitor via the API
SSL monitoring is a standalone ssl-type monitor, separate from HTTP uptime checks. Use ssl_expiry_alert_days to set how many days before expiry the alert fires.
# Create an SSL certificate monitor (type: ssl)
curl -X POST https://miterl.com/api/v1/monitors \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Client Site SSL",
"type": "ssl",
"url": "https://client-site.com",
"interval_seconds": 300,
"ssl_expiry_alert_days": 30
}'
The ssl_expiry_alert_days setting controls how many days before expiry the alert fires. Set it to 30 to schedule renewal early; add a second SSL monitor with a 7-day threshold for a final confirmation check. Run your HTTP uptime check as a separate type: "http" monitor alongside it.
Recommended multi-threshold alert setup
| Alert Threshold | Purpose |
|---|---|
| 30 days out | Confirm auto-renewal config is intact; assign renewal owner |
| 14 days out | Verify renewal work is scheduled or already done |
| 7 days out | Final reminder; escalate if still pending |
| 1 day out | Emergency response trigger |
Bulk-Check SSL Status Across All Client Sites
When you manage 20 or 30 client sites, checking each one individually is impractical. Pull all monitor statuses at once via the API to quickly identify any that need attention.
# List all monitors with name, status, and 30-day uptime
curl -s "https://miterl.com/api/v1/monitors?per_page=100" \
-H "Authorization: Bearer YOUR_API_KEY" \
| jq -r '.data[] | "\(.name)\t\(.status)\t\(.uptime_30d)%"'
If an SSL-type monitor shows down, the certificate has either expired or there is a chain validation error — act immediately. A down status before ssl_expiry_alert_days is hit means the certificate itself is invalid, not just approaching expiry.
Recommended Workflow for Agencies
A practical SSL monitoring workflow for agencies managing multiple client sites:
- Register all client sites in Miterl with SSL monitoring enabled
- 30-day alert -- verify the auto-renewal configuration is intact and schedule any manual renewals
- 7-day alert -- confirm renewal has completed successfully; escalate if it has not
- Monthly reporting -- include certificate status in your client maintenance reports as proof of proactive management
Including SSL certificate expiry dates in your monthly client reports makes the "what are we doing for you each month?" question easy to answer with a concrete, visible deliverable. For how to build and automate those monthly reports, see "Automating SLA Reports for Maintenance Subscriptions."
Do Not Skip the Basics: Verify Auto-Renewal
Alongside monitoring, periodically verify that your auto-renewal setup is actually working.
# Test certbot auto-renewal without actually renewing
sudo certbot renew --dry-run
# Check the systemd timer status
systemctl status certbot.timer
# Review recent renewal logs
sudo journalctl -u certbot.timer --since "30 days ago"
Even when auto-renewal is working perfectly, monitoring serves as your safety net. The assumption that "it should be renewing automatically" is exactly the kind of assumption that causes outages at 2 AM on a Saturday.
Use Heartbeat Monitoring to Verify certbot Actually Ran
You can take SSL safety one step further by using heartbeat monitoring to confirm that certbot's renewal process executed — not just that it was scheduled to run. Add a Miterl heartbeat ping to certbot's --post-hook:
# Add to /etc/cron.d/certbot or as a systemd timer hook
# The heartbeat fires only when renewal succeeds
0 0,12 * * * root certbot renew --quiet \
--post-hook "curl -s https://miterl.com/api/v1/monitors/YOUR_HEARTBEAT_MONITOR_ID/heartbeat \
-H 'Authorization: Bearer YOUR_API_KEY' > /dev/null"
If certbot runs but fails silently, the heartbeat never fires and Miterl alerts you. This closes the gap between "the timer ran" and "the renewal actually succeeded." For more on heartbeat monitoring patterns, see "Heartbeat Monitoring: How to Verify Cron Jobs Are Running."
Managing SSL Monitoring Alongside HTTP and DNS
Agencies often start with a dedicated SSL checking tool separate from their uptime monitoring. As your client count grows, the fragmentation creates a practical problem: when a site goes down due to certificate expiry, you are correlating alerts from two different dashboards before you can act. Consolidating HTTP, SSL, and DNS monitoring into one tool eliminates that delay. For a direct comparison of unified versus distributed monitoring architectures, see "Unified Monitoring vs. Separate Tools: Which Is Right for Your Agency?."
Pre-Launch SSL Checklist
For new site launches, SSL configuration is one of the most important items to verify before the domain goes live. The "Pre-Launch Monitoring Checklist" includes SSL verification steps alongside HTTP, DNS, and heartbeat setup in a single launch-day workflow.
Summary
SSL certificate expiry is not prevented by auto-renewal alone. Multiple failure modes exist, and the only reliable defense is layered: keep auto-renewal healthy and monitor independently.
- Expiry causes include missed cron migration, DNS validation failure, outdated certbot, and manually-issued certs with no auto-renewal
- Set up SSL monitors in Miterl across all client sites with 30-day and 7-day thresholds
- Run
certbot renew --dry-runmonthly to verify auto-renewal health - Use heartbeat monitoring on the certbot post-hook to confirm successful execution, not just scheduled execution
- Include certificate status in monthly client reports to demonstrate proactive maintenance value
WordPress hosting environments add several failure patterns on top of these general causes — including cache plugins that intercept ACME challenge URLs and security plugins that block certbot. "SSL Certificate Monitoring for WordPress Agencies" covers those WordPress-specific patterns and walks through bulk SSL management across multiple client sites.
For more details on SSL monitoring configuration, see the documentation. Try Miterl's monitoring in action by signing up for free. If you have questions before getting started, check the FAQ.
When an SSL certificate expires, the symptom at the HTTP layer is a connection failure or a 5xx response — which means your HTTP uptime monitors will fire at the same time as your SSL expiry alert. Understanding how to interpret and act on these HTTP-level signals is covered in "HTTP Response Monitoring: Detect Failures by Status Code."