Your SSL certificate expired at 2am on a Tuesday. By the time monitoring caught it, Google had flagged the site insecure, users were bouncing off browser warnings, and the mail server was rejecting TLS connections. The renewal took five minutes. Knowing it was about to expire takes one script running on a cron.
What Does the SSL Certificate Expiry Script Look Like?
What each part does
set -euo pipefail— exit on error, unset variable, or broken pipe. A cert check that silently errors is worse than no check.WARN_THRESHOLD=30— 30 days gives two renewal attempts before expiry on a 90-day Let's Encrypt cert. Raise to 45 for tighter policies.DOMAINS=(...)— list every domain and subdomain explicitly. Wildcards in this array won't expand to real hostnames.-servername "${DOMAIN}"— the SNI flag. On servers hosting multiple domains from one IP, without this flag OpenSSL returns the server's default cert instead of the one for your domain.echo |— preventsopenssl s_clientfrom waiting for stdin in interactive mode. Required for cron and CI pipelines.cut -d= -f2—enddateoutputsnotAfter=Jun 15 12:00:00 2026 GMT; cutting on=isolates the parseable date string.date -d/date -j -f— Linux vs macOS BSD date syntax. The||fallback handles both without an OS check.(EXPIRY_EPOCH - NOW_EPOCH) / 86400— integer arithmetic drops fractional days, making the result slightly conservative — the right direction.
How Does the SSL Expiry Check Work Step by Step?
The script establishes a real TLS connection to your server — the same handshake a browser performs — and reads the certificate that the server actually presents. This catches two failure modes that static cert file checks miss: the wrong cert being served (SNI misconfiguration) and the live cert differing from what you think you deployed.
The date arithmetic converts both the expiry date and today's date to Unix epoch seconds, then divides the difference by 86400. Integer division drops fractional days, so a cert expiring in 29.9 days reports as 29 — slightly early, which is the safe direction.
What Are the Common Variations?
Check a non-standard port (SMTP TLS, LDAPS):
Read domains from a file:
Send an email when threshold is breached:
How Do I Schedule the SSL Check Automatically?
Daily at 8am. The log gives you a history of when warnings started firing — useful for tracking renewal lead times over months.
Test the full pipeline before you rely on it
Run the script manually against a domain you know the expiry for, and verify the output matches. Then confirm your cron job actually executes by checking /var/log/ssl-check.log the next day. A monitoring script that silently fails to run is no monitoring at all. Pair this with the website uptime check script so you catch both the pre-expiry warning and any live downtime.
Frequently Asked Questions
Does this work with Let's Encrypt certificates?
Yes. openssl s_client connects to the live server and reads whatever certificate is currently being served — Let's Encrypt, DigiCert, Comodo, or self-signed. The expiry extraction is identical for all CAs.
Can I check multiple domains in one run?
Yes. The DOMAINS array handles any number of entries. Each domain gets its own status line. Add subdomains individually — wildcard entries won't expand to real hostnames.
Why does the script use echo | and 2>/dev/null?
openssl s_client waits for stdin in interactive mode. In a cron job with no terminal, this causes the script to hang indefinitely. The echo | redirect sends empty stdin so the command closes after reading the certificate.
My server returns the wrong certificate — how do I fix it?
Add -servername your.domain.com to the openssl s_client call — the script already does this via -servername "${DOMAIN}". Servers using SNI host multiple certs from one IP; without the flag, OpenSSL gets the server's default.
What port do I use for SMTP or LDAPS?
Replace 443 with 465 for SMTP+TLS, 8443 for alternate HTTPS, 636 for LDAPS. The -connect syntax is always host:port.
Part of the Server Monitoring guide · Linux Security guide
Monitor SSL expiry on your DigitalOcean droplets — catch the silent failure before browsers do.
Get $200 free credit — DigitalOcean
Get $200 Free →Affiliate link · we earn a commission
Related Scripts
- Check If Website Is Up — curl HTTP status check, cron-schedulable for five-minute uptime monitoring
- SSH Key Setup Script — automate ed25519 key generation and authorized_keys deployment
- Restart Service If Stopped — systemctl watchdog that recovers crashed services within 60 seconds