SSL certificate expiry is one of the most preventable outages in web development. Every year, major sites go dark because a certificate expired and nobody noticed — not because the renewal was impossible, but because the alert never fired. The tools available today fall into two broad categories: free checkers you run manually, and automated monitors that alert you before expiry happens. Choosing the right one depends on how many domains you manage, how much production traffic is at stake, and whether you trust yourself to check on a schedule. This guide covers both ends of the spectrum honestly.
Manual checkers are fast, require no signup, and are perfectly adequate for a small number of personal or low-stakes sites. The trade-off is simple: you have to remember to check, and most people don't.
The fastest approach — no account, no browser, no setup. If you have openssl installed (it comes pre-installed on macOS and most Linux distros), you can check any domain in seconds:
# Show full validity range (notBefore + notAfter)
openssl s_client -connect domain.com:443 2>/dev/null | openssl x509 -noout -dates
# Show only the expiry date
echo | openssl s_client -servername domain.com -connect domain.com:443 2>/dev/null \
| openssl x509 -noout -enddate
Output from the second command looks like:
notAfter=Dec 14 23:59:59 2026 GMT
To get the number of days remaining directly (Linux):
expiry=$(echo | openssl s_client -servername domain.com -connect domain.com:443 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
echo $(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 )) days remaining
The -servername flag enables SNI, which is required for virtual-hosted servers. Skip it and you may get the wrong certificate back on shared-IP servers.
Every modern browser exposes certificate details behind the padlock (or site-info) icon in the address bar. Click it, then look for Connection is secure → Certificate is valid (Chrome/Edge) or the equivalent in Firefox/Safari. Drill in and you'll see the issuer, issued date, and expiry date.
This works well for a one-off check on a site you're currently visiting. It doesn't scale to checking a list of domains, and it only shows the cert the browser received — not intermediate certs in the chain.
Web-based SSL checkers let you enter a domain and get expiry details without installing anything. They're useful when you're on a machine without openssl or when you want a second opinion.
Our own SSL Certificate Checker — free, no signup shows days remaining, expiry date, issuer, and whether the chain is valid. Results appear in seconds with no account required.
For deeper analysis — cipher suites, chain completeness, protocol support, and a letter-grade rating — SSL Labs Server Test (ssllabs.com/ssltest) is the most thorough free option. It takes 30–90 seconds per scan and is worth running before a production launch or after a cert renewal.
All manual checkers share the same failure mode: you have to remember to use them. Most teams don't. Expiries sneak up during busy periods, after team transitions, or simply because nobody owns the "check SSL certs" calendar reminder. A cert that was 60 days out last month is now 7 days out, and nothing fired.
Manual SSL checking works until it doesn't. Here are the signs you need something that runs on its own:
Once you decide to automate, there are several approaches ranging from self-hosted scripts to purpose-built services. Here's how they compare.
The lowest-cost automated option: a shell script that checks cert expiry using openssl and sends an email alert, scheduled via cron. Here's a working example:
#!/bin/bash
# ssl-monitor.sh — check SSL expiry and alert if within WARN_DAYS
DOMAINS=("example.com" "api.example.com" "staging.example.com")
WARN_DAYS=30
ALERT_EMAIL="ops@example.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 "SSL check failed: $domain" \
| mail -s "SSL FAIL: $domain" "$ALERT_EMAIL"
continue
fi
days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
if [ "$days" -le "$WARN_DAYS" ]; then
echo "$domain expires in $days days ($expiry)" \
| mail -s "SSL WARNING: $domain expires in $days days" "$ALERT_EMAIL"
fi
done
Schedule it in crontab to run daily:
# Run SSL check daily at 8 AM
0 8 * * * /usr/local/bin/ssl-monitor.sh >> /var/log/ssl-monitor.log 2>&1
Strengths: free, no external dependencies, auditable, runs on infrastructure you control.
Weaknesses: the script itself can fail silently (cron stops, server migrates, mail server is down). There's no watchdog on the watchdog. You also have to keep the domain list updated manually, and every new domain added to your infrastructure is a manual step away from a gap.
If your team already lives in GitHub, a scheduled workflow is an attractive option — no server needed, visible to the whole team, and free within GitHub Actions limits. Here's a working workflow:
name: SSL Certificate Expiry Check
on:
schedule:
- cron: '0 8 * * *' # daily at 8 AM UTC
workflow_dispatch: # allow manual trigger
jobs:
check-ssl:
runs-on: ubuntu-latest
steps:
- name: Check SSL certificates
run: |
DOMAINS=("example.com" "api.example.com" "staging.example.com")
WARN_DAYS=30
FAILED=0
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 "FAIL: Could not retrieve cert for $domain"
FAILED=1
continue
fi
days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
echo "$domain: $days days remaining (expires $expiry)"
if [ "$days" -le "$WARN_DAYS" ]; then
echo "WARNING: $domain expires in $days days"
FAILED=1
fi
done
exit $FAILED
When the workflow fails (any cert within 30 days), GitHub sends a failure notification to whoever has notifications enabled. You can extend this with Slack notifications via actions/github-script or a dedicated Slack action.
Strengths: no server dependency, team-visible, integrates with existing GitHub notification preferences, free.
Weaknesses: GitHub Actions checks originate from GitHub's infrastructure — if your domain is IP-allowlisted, you'll need to handle that. The domain list is still manual. Alerts arrive as workflow failure notifications, which blend in with CI failures and get ignored over time.
General-purpose uptime monitors like UptimeRobot, BetterStack, and Freshping include SSL expiry checking as part of their HTTP monitor feature. If you're already paying for uptime monitoring, check whether SSL alerting is included in your plan — it often is, as a secondary feature.
The typical setup: add a monitor for https://example.com, and the service checks both uptime and certificate expiry. Alert thresholds vary by platform — some only alert at 7 days, others offer configurable windows.
Strengths: one tool for uptime + SSL, familiar interface if you're already using one of these services.
Weaknesses: SSL alerting is a secondary feature, not the primary focus. Alert lead times are often shorter than ideal, and the multi-cert management workflow isn't purpose-built.
Purpose-built SSL monitoring tools handle the full lifecycle: scheduled external checks, configurable alert windows (30/14/7/1 days is a common pattern), multi-domain dashboards, and alert routing to email and Slack. The external-check architecture means monitoring continues even if your server is down.
SSLWatch is a dedicated SSL monitoring tool built exactly for this: add your domains, and it checks them on a schedule and sends alerts at 30, 14, 7, and 1 day before expiry. No cron job to configure, no domain list to keep synchronized with a script, no server dependency. The monitoring keeps running regardless of your infrastructure state.
If you're evaluating dedicated SSL monitors, here are the features that actually matter:
| Feature | Why it matters |
|---|---|
| Alert lead time | 30+ days is ideal for managed certificates — you need time to request, validate, and deploy. 14+ days for Let's Encrypt, where auto-renewal should catch it but might not. |
| Multi-domain support | A monitor that only handles one domain at a time creates the same scaling problem as manual checking. Look for bulk import or a clean dashboard view across all domains. |
| Alert channels | Email is a baseline. Slack integration is important for teams that already route operational alerts there. PagerDuty or webhook support matters for on-call workflows. |
| External check architecture | The monitor should run from infrastructure independent of yours. A monitoring script on your own server stops working the moment your server has problems — which is exactly when you need monitoring most. |
| Dashboard visibility | A view of all your domains and their days remaining is useful beyond just alerts — it lets you proactively review the cert portfolio and catch anything that slipped through. |
| Price | For small teams and indie developers, $9/month or less is a reasonable ceiling. SSL monitoring is infrastructure hygiene, not a growth tool — the price should reflect that. |
There's no single correct answer — the right tool depends on your context:
Check any domain instantly — days remaining, issuer, expiry date. No signup required.
Check your SSL certificate →Monitor your domains and get alerted at 30, 14, 7, and 1 days before expiry. No more manual checking.
Join the SSLWatch waitlist →