SSL certificate expiry is one of the most embarrassing outages a server operator can cause. The cert works perfectly until midnight on expiry day, then every visitor gets a browser warning and your site looks compromised. The fix takes five minutes — but only if you know it's coming.
This guide covers three things: how to check nginx SSL certificate expiry from the command line right now, how to read the expiry date from your nginx configuration directly, and how to set up a cron-based alert so you get notified before the expiry date arrives.
The fastest way to check an SSL certificate expiry date from the command line is openssl s_client. This connects to the live server and reads the certificate it serves — exactly what a browser would see:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates
You'll get output like:
notBefore=Aug 10 12:00:00 2026 GMT
notAfter=Nov 8 12:00:00 2026 GMT
The notAfter field is your expiry date. The -servername flag is important — it enables SNI (Server Name Indication), which nginx uses to serve the correct certificate when multiple domains share the same IP. Without it, nginx may return the wrong certificate.
To get just the number of days remaining:
EXPIRY=$(echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "Days until expiry: $DAYS"
date -d flag is Linux (GNU coreutils). On macOS, use date -j -f instead. The script above handles both — the 2>/dev/null || falls back to the macOS syntax if the Linux one fails.
If you have local access to the server, you can inspect the certificate file referenced in your nginx config without making a network connection. This is useful for scripting on the server itself or for checking certs that aren't yet serving traffic.
Find your certificate path in the nginx config:
grep ssl_certificate /etc/nginx/sites-enabled/* /etc/nginx/nginx.conf 2>/dev/null
Then read the expiry date directly from the file:
# Replace with your actual certificate path
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -enddate
For Let's Encrypt certificates managed by Certbot, you can also use Certbot's own reporting:
certbot certificates
This lists all managed certificates with their expiry dates and renewal status, including whether auto-renewal is configured and when it last ran.
Checking manually works once. A cron job that runs weekly and sends you an email when expiry is within 30 days works indefinitely.
Create a script at /usr/local/bin/check-ssl-expiry.sh:
#!/bin/bash
# check-ssl-expiry.sh — alert when SSL cert expires within WARN_DAYS
DOMAIN="example.com"
WARN_DAYS=30
ALERT_EMAIL="ops@example.com"
EXPIRY=$(echo | openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
if [ -z "$EXPIRY" ]; then
echo "SSL check failed: could not retrieve certificate for $DOMAIN" \
| mail -s "SSL check failed: $DOMAIN" "$ALERT_EMAIL"
exit 1
fi
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS" -le "$WARN_DAYS" ]; then
echo "SSL certificate for $DOMAIN expires in $DAYS days ($EXPIRY)." \
| mail -s "ACTION REQUIRED: SSL cert for $DOMAIN expires in $DAYS days" "$ALERT_EMAIL"
fi
Make it executable and add it to crontab:
chmod +x /usr/local/bin/check-ssl-expiry.sh
# Add to crontab — runs every Monday at 8am
crontab -e
# Add this line:
0 8 * * 1 /usr/local/bin/check-ssl-expiry.sh
Test it before you rely on it:
# Test with a shorter warning window to trigger the email
WARN_DAYS=9999 bash /usr/local/bin/check-ssl-expiry.sh
WARN_DAYS value when first testing to confirm you actually receive the alert email before lowering it to 30.
One detail that trips up nginx operators: when you renew a Let's Encrypt certificate, nginx continues serving the old certificate from memory until you reload it. The renewal itself doesn't cause downtime, but the swap does require a reload.
Certbot's nginx plugin handles this automatically with a --deploy-hook. If you're managing renewal manually or using a different renewal tool, add a reload step:
# In your renewal script or cron job, after the renewal succeeds:
nginx -t && systemctl reload nginx
# Or with certbot deploy hook:
certbot renew --deploy-hook "nginx -t && systemctl reload nginx"
Always run nginx -t before reloading — it validates the config and prevents a reload that would take nginx down if the config has errors. The && ensures the reload only happens if the test passes.
The openssl + cron approach works well for a handful of domains you manage directly. It breaks down when:
For simple cases, the bash script above is sufficient. For anything beyond two or three domains with real production traffic, external monitoring avoids the failure mode where the monitoring tool and the monitored service go down together.
Paste your domain into the free SSL Certificate Checker to see expiry date, issuer, chain validity, and how many days remain. No signup required.
Check SSL certificate → Get expiry alerts with SSLWatch