← Blog  ·  October 2026

SSL Certificate Expiry Monitoring: How to Get Alerts Before It Expires

Free and paid approaches for developers who can’t afford an unexpected outage.

The cost of missing an expiry: An expired SSL certificate shows a browser warning that most users won’t click past. Search engines de-index HTTPS pages that return errors. Payment processors refuse to connect. A single missed renewal can take down a production service for hours.

SSL certificate expiry is one of those problems that looks trivial until it bites you. Let’s Encrypt renewals are automatic — until something breaks in the automation and nobody notices for 30 days. Manually managed certificates expire when your calendar reminder gets ignored or the person who set the reminder left the company.

This guide covers every meaningful approach to SSL certificate expiry monitoring, from a free five-minute cron job to paid alerting services, so you can pick the right level of coverage for each environment.


Check a Certificate’s Expiry Right Now

Before setting up automated monitoring, it’s useful to know where you stand. You can check any domain’s certificate expiry in your browser’s address bar, or use the command line:

echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -dates

This returns the notBefore and notAfter dates directly. For a human-readable days-remaining count:

echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
  | openssl x509 -noout -enddate \
  | awk -F= '{print $2}' \
  | xargs -I{} date -d "{}" +%s \
  | xargs -I{} sh -c 'echo $(( ({} - $(date +%s)) / 86400 )) days remaining'

Or use the free SSL Certificate Checker tool to get a visual result instantly without touching the command line.


Option 1: A Simple Cron Job (Free, 10 Minutes to Set Up)

The most straightforward DIY approach is a cron job that checks certificate expiry and sends an email when the deadline gets close. Here’s a working shell script:

#!/bin/bash
# ssl-check.sh — checks a domain's cert and alerts if expiry < 30 days

DOMAIN="yourdomain.com"
ALERT_DAYS=30
ALERT_EMAIL="you@example.com"

EXPIRY=$(echo | openssl s_client -connect "$DOMAIN":443 -servername "$DOMAIN" 2>/dev/null \
  | openssl x509 -noout -enddate 2>/dev/null \
  | cut -d= -f2)

if [ -z "$EXPIRY" ]; then
  echo "SSL check failed for $DOMAIN — could not retrieve certificate" \
    | mail -s "SSL check error: $DOMAIN" "$ALERT_EMAIL"
  exit 1
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 "$ALERT_DAYS" ]; then
  echo "SSL certificate for $DOMAIN expires in $DAYS_LEFT days ($EXPIRY)" \
    | mail -s "⚠️ SSL expiry warning: $DOMAIN ($DAYS_LEFT days)" "$ALERT_EMAIL"
fi

Schedule it to run daily at 9am:

0 9 * * * /path/to/ssl-check.sh

This works reliably if your server has openssl and mail configured. The limitations show up quickly in practice:

For a single personal domain, this is fine. For production services or multiple domains, you need something more robust.


Option 2: curl + openssl in a Cron Job with Webhook Alerting

A slightly more robust approach replaces the mail dependency with a webhook POST to Slack or a generic HTTP endpoint:

#!/bin/bash
DOMAIN="yourdomain.com"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
ALERT_DAYS=30

EXPIRY=$(echo | openssl s_client -connect "$DOMAIN":443 2>/dev/null \
  | openssl x509 -noout -enddate | cut -d= -f2)
DAYS_LEFT=$(( ( $(date -d "$EXPIRY" +%s) - $(date +%s) ) / 86400 ))

if [ "$DAYS_LEFT" -le "$ALERT_DAYS" ]; then
  curl -s -X POST "$SLACK_WEBHOOK" \
    -H "Content-Type: application/json" \
    -d "{\"text\": \"⚠️ SSL expiry: $DOMAIN expires in $DAYS_LEFT days\"}"
fi

This removes the dependency on a local mail server, which is usually the point of failure in the first version. The cron job reliability problem remains: if the job silently stops running, your next alert about the cert is the browser warning your users see.


Option 3: Let’s Encrypt with Certbot Auto-Renewal

If you’re using Let’s Encrypt, the right answer is usually to make sure certbot renew is running correctly rather than monitoring expiry dates manually. A working auto-renewal setup makes the problem go away.

Check the status of your renewal timer:

# systemd (most modern Linux):
systemctl status certbot.timer

# or for snap-installed certbot:
systemctl status snap.certbot.renew.timer

# verify the renewal would succeed (dry run):
certbot renew --dry-run

The catch is that certbot renew can silently fail. Common failure modes:

Running --dry-run manually every few months catches most of these, but it doesn’t give you proactive alerting when something breaks between manual checks.

Best practice for Let’s Encrypt: Run certbot renew --dry-run in a cron job and alert on failure. This tests the renewal path without actually renewing, so it fails early rather than 30 days before expiry.

Option 4: Cloud Provider Built-In Monitoring

If your certificates are managed through AWS Certificate Manager (ACM), Google Cloud Certificate Manager, or Cloudflare, those platforms have built-in expiry notifications:

If you’re already on one of these platforms and using their certificate management, turn on their notifications. They’re free and require almost no setup.


Option 5: Free External Monitoring Services

Several services will check your certificate from outside your infrastructure and email you before it expires. The notable free options:

Service Free tier Alert advance Channels
UptimeRobot 50 monitors, 5-min interval 30 days Email, Slack, webhook
StatusCake 10 SSL monitors 30 days Email, webhook
Better Uptime 10 monitors Configurable Email, SMS, Slack
Oh Dear 14-day trial only Configurable Email, Slack, webhook
SSLWatch Free tool, $9/mo for alerts 60/30/7 days Email alerts

UptimeRobot’s free tier is the most commonly used option for indie developers: 50 monitors with 5-minute ping intervals and basic SSL expiry alerts. The SSL monitoring is a secondary feature rather than the primary focus, but it works reliably for most use cases.


Option 6: Dedicated SSL Certificate Monitoring

For production environments where an expired certificate means real revenue impact, dedicated SSL monitoring tools offer more thorough coverage:

SSLWatch — Automated SSL Certificate Expiry Alerts

Get email alerts 60, 30, and 7 days before any certificate expires. Never manually track renewal dates again. Add any domain in seconds.

Join the waitlist Free checker tool

What Approach Should You Use?

The right answer depends on your infrastructure:


The Monitoring Gap Most Teams Miss

There’s a common misconception that Let’s Encrypt auto-renewal makes SSL monitoring unnecessary. Let’s Encrypt renews certificates automatically — when the automation is working. The automation can break silently, and by the time you notice, you have 30 days left instead of 90.

The pattern that catches this: monitor the certificate expiry date externally, independent of whether you think auto-renewal is working. If the cert’s expiry date stops advancing (i.e., it’s no longer renewing), you’ll know 60 days before the expiry rather than on the day it fails.

This is why external monitoring is worth adding even if you have certbot configured: it validates that the automation is actually doing its job, not just that the job is scheduled to run.


Checking Certificate Expiry in CI

One underused approach: add an SSL check to your deployment pipeline. After deploying, verify the certificate is valid and not within 30 days of expiry:

#!/bin/bash
# Add to your post-deploy CI step
DOMAIN="yourdomain.com"
DAYS_LEFT=$(echo | openssl s_client -connect "$DOMAIN":443 2>/dev/null \
  | openssl x509 -noout -enddate \
  | cut -d= -f2 \
  | xargs -I{} date -d "{}" +%s \
  | xargs -I{} sh -c 'echo $(( ({} - $(date +%s)) / 86400 ))')

if [ "$DAYS_LEFT" -lt 30 ]; then
  echo "::warning::SSL certificate expires in $DAYS_LEFT days"
fi

This surfaces certificate issues in your deploy logs where developers are already looking, without requiring any external tooling. It complements rather than replaces dedicated monitoring — CI runs on deploy, not daily, so it won’t catch a cert that expires between deploys.


Summary

SSL certificate expiry is preventable with minimal investment. The key points:

  1. If you use Let’s Encrypt, run certbot renew --dry-run in a cron job that alerts on failure — this catches renewal breakage early rather than 30 days before expiry.
  2. If you use a cloud provider’s certificate management (ACM, Cloudflare), turn on their built-in expiry notifications today.
  3. For any production domain, add an external monitoring check that alerts at multiple advance windows (60, 30, 7 days). External monitoring validates that your automation is working, not just that it’s scheduled.
  4. Check your current certs using the free SSL Certificate Checker to see where you stand before setting up monitoring.

Never track SSL renewal dates manually again

SSLWatch monitors your certificates and sends automated email alerts at 60, 30, and 7 days before expiry. Simple setup, $9/month.

Get early access Check your cert free