DEV Community

DevHelm
DevHelm

Posted on • Originally published at devhelm.io

SSL Certificate Monitoring: Prevent Outages Before Your Certs Expire

A Let's Encrypt certificate renews every 90 days. When auto-renewal fails silently — a DNS record changed, an ACME challenge path broke during a migration, a permissions change on the webroot — your users see a browser security warning and your site is effectively down. Browsers refuse to load the page, API clients reject the connection, and mobile apps show a blank error screen.

The frustrating part: every one of these outages is preventable. Certificate expiry is not unpredictable. The expiration date is baked into the certificate itself, readable by any TLS client. The only reason teams get bitten is that nobody was checking.

Why SSL certificates expire (and why auto-renewal fails)

TLS certificates have a maximum validity period — 90 days for Let's Encrypt, up to 398 days for commercial CAs. This is by design: shorter lifetimes reduce the window during which a compromised private key can be exploited.

Most teams rely on automated renewal through ACME clients like certbot, acme.sh, or cloud-native solutions (AWS ACM, Cloudflare Origin CA). When these work, you never think about certificate expiry. When they fail, the failure is silent.

Common failure modes:

DNS changes that break ACME challenges. You migrate DNS from one provider to another. The ACME DNS-01 challenge was configured for the old provider's API. Certbot tries to renew, cannot create the required TXT record, and silently gives up. The certificate continues serving for 60 more days until it expires.

Infrastructure migrations that break file paths. You move from nginx to a reverse proxy. The /.well-known/acme-challenge/ path no longer routes to the certbot webroot. HTTP-01 challenges fail. The cert renewal log shows errors, but nobody is watching that log.

Permissions and credential rotation. Cloud-provider IAM credentials used by the ACME client get rotated. The renewal script runs as a different service account than when it was first configured. It fails with a 403, and the retry logic gives up after three attempts.

Vendor certificates you do not control. A SaaS dependency serves its API behind a certificate you cannot renew. A CDN edge node has its own certificate lifecycle. A legacy internal service uses a certificate issued by an internal CA with its own renewal cadence. None of these are in your automation — they expire on their own schedule.

What SSL certificate monitoring actually checks

A useful SSL monitoring system goes beyond "is the certificate expired right now." By the time the answer is "yes," the outage is already happening. Meaningful checks include:

Days until expiry. The primary signal. Alerting at 30 days gives your team time to investigate a broken renewal pipeline before it becomes an outage. Alerting at 14 days is the escalation threshold — something is wrong, and it needs attention today.

Certificate chain validity. An expired intermediate certificate breaks the chain even when the leaf certificate is current. Older clients (Android < 7.1, some embedded devices) that do not perform AIA fetching will reject a connection with a missing intermediate.

Hostname matching. A certificate issued for *.example.com does not cover example.com (the bare apex) unless explicitly included as a SAN. After a CDN migration or load balancer swap, the served certificate may not match the requested hostname.

Issuer changes. If the certificate issuer changes unexpectedly — say, your Let's Encrypt cert is suddenly signed by an unknown CA — that is a signal worth investigating. It may indicate a CDN misconfiguration, a MITM proxy in the path, or a compromised renewal pipeline.

When you need dedicated SSL monitoring

If you have a single domain with one certificate behind a managed provider (ACM, Cloudflare), and your infrastructure never changes, you might survive without monitoring. Everyone else needs it:

Multiple domains and subdomains. Each certificate has its own renewal lifecycle. Ten domains means ten independent renewal processes, each with its own failure modes. One forgotten subdomain is all it takes.

Wildcard certificates. Wildcards cover *.example.com but not nested subdomains (api.staging.example.com). Teams assume the wildcard covers everything, discover the gap at 2 AM when the staging API breaks.

Internal services with self-signed certificates. Internal CA certificates have their own expiry. The internal PKI that issues them may not have automated renewal. The operations team that set it up two years ago may no longer be on the team.

Vendor certificates you depend on. Your payment gateway, your authentication provider, your CDN — all serve certificates you cannot renew. If their certificate expires or their chain breaks, your integration fails. Monitoring their certificate from outside tells you about the problem before your on-call pager does.

Approaches to SSL monitoring

Manual calendar reminders. You look up the expiration date, set a calendar event 30 days before, and hope whoever gets the reminder knows what to do. This breaks the moment someone changes teams, the domain list grows, or the certificate gets replaced early (resetting the expiry date without updating the calendar).

Cron scripts with openssl. A bash script runs openssl s_client -connect example.com:443 and parses the notAfter date. This works until the script's host is down, the output format changes between OpenSSL versions, or the alert channel it writes to gets archived. It also only checks from one location — useless for catching regional CDN cert issues.

Assertion-based monitoring. An HTTP monitor runs from multiple regions on a fixed schedule and includes an SSL expiry assertion alongside its other checks — status code, response time, body content. The SSL check is part of the monitor, not a separate system. When the certificate drops below the threshold, it triggers the same alert pipeline as a 500 error or a timeout. This is the approach that scales.

Setting up SSL monitoring with assertions

DevHelm's HTTP monitors support an ssl_expiry assertion that checks the certificate's remaining validity on every request. You configure the minimum days remaining, and the assertion fails when the certificate crosses that threshold.

A YAML configuration that monitors a production API with both a warning and a failure threshold:

name: Production API SSL
type: http
url: https://api.example.com/health
frequency: 300s
regions:
  - us-east
  - eu-west
  - ap-southeast
assertions:
  - type: status_code
    value: 200
  - type: ssl_expiry
    minDaysRemaining: 30
    severity: warn
  - type: ssl_expiry
    minDaysRemaining: 14
    severity: fail
Enter fullscreen mode Exit fullscreen mode

The same monitor via CLI:

devhelm monitor create \
  --type http \
  --url https://api.example.com/health \
  --frequency 300 \
  --regions us-east,eu-west,ap-southeast \
  --assertion "status_code=200" \
  --assertion "ssl_expiry>=30"
Enter fullscreen mode Exit fullscreen mode

The minDaysRemaining: 30 threshold fires a warning when the certificate has 30 days left — enough time to investigate why auto-renewal is failing, fix the issue, and verify the fix before the certificate actually expires. The minDaysRemaining: 14 threshold fires a failure alert — this is the escalation point.

Multi-region coverage matters here. A CDN edge node may have an expired or misconfigured certificate in one region while other regions serve a valid cert. If you only monitor from one location, you will not see the problem until users in that region report it. Running the assertion from multiple regions catches the discrepancy — the same monitor passes from us-east and fails from eu-west, telling you exactly where the problem is.

For teams managing their monitors as code, the ssl_expiry assertion fits into the same version-controlled config as your other monitoring definitions — reviewed in PRs, deployed through CI, reproducible across environments. See monitoring as code for the full workflow.

Beyond expiry: what else to monitor

Certificate expiry is the most common failure, but it is not the only one:

Certificate chain changes. If the chain your server presents changes — different intermediate, different root, different leaf issuer — that is worth alerting on. It can indicate a CDN misconfiguration (wrong origin pull), a man-in-the-middle proxy that was not there yesterday, or an unintended renewal that picked up a different issuer. For more on what SSL errors mean and how to diagnose them, the chain is usually where it starts.

Protocol downgrades. A server that suddenly negotiates TLS 1.0 instead of TLS 1.3 may have a misconfigured load balancer or a fallback rule that should not be active. Compliance frameworks (PCI DSS 4.0) require TLS 1.2 minimum — a protocol downgrade is a compliance violation before it is a security one.

Mixed content and HSTS gaps. A site that serves HTTPS but loads resources over HTTP gets degraded in browsers. If your monitoring confirms the TLS connection is valid but users still see warnings, the problem may be mixed content rather than the certificate itself.

Building SSL monitoring into your stack

SSL certificate monitoring is not a separate discipline. It is one assertion on an HTTP monitor you are probably already running. The same monitor that checks your API returns a 200 in under 500ms can also check that the certificate will not expire in the next 30 days, using the same alert pipeline and the same on-call routing.

For a broader view of how SSL monitoring fits alongside uptime, latency, and content checks, see the best website monitoring tools comparison.

The practical setup for most teams: add an ssl_expiry assertion with minDaysRemaining: 30 to every external-facing HTTP monitor. For critical services — payment endpoints, authentication providers, API gateways — add a second assertion at 14 days with failure severity. For vendor dependencies you cannot renew yourself, the monitoring is your only early-warning system.

Add an ssl_expiry assertion to any HTTP monitor in 60 seconds — from the dashboard, the CLI, Terraform, or YAML config. Start at app.devhelm.io, free for your first 50 monitors.


Originally published on DevHelm.

Top comments (0)