Why Certificates Expire
Certificates have expiration dates for security reasons. If a private key is compromised, the damage is limited to the certificate's validity period. Shorter lifetimes also force regular key rotation.
Common validity periods: - Let's Encrypt: 90 days - Commercial DV/OV: 1 year (397 days max since 2020) - Self-signed / internal: Whatever you set (common: 1-10 years)
An expired certificate doesn't just show a browser warning -- it breaks API calls, webhook deliveries, mobile apps, and automated systems that validate certificates strictly.
Check Expiration from the Command Line
Check a remote server
echo | openssl s_client -connect example.com:443 -servername example.com \
2>/dev/null | openssl x509 -noout -dates
Output:
notBefore=Jan 15 00:00:00 2026 GMT
notAfter=Apr 15 23:59:59 2026 GMT
Check a local certificate file
openssl x509 -in cert.pem -noout -enddate
Check days until expiry
expiry=$(openssl x509 -in cert.pem -noout -enddate | cut -d= -f2)
days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
echo "$days days until expiry"
Check via our SSL checker
Paste your domain into getaCert.com/check to see the full certificate details including expiration date.
Simple Monitoring with Cron
A bash script that checks your domains and emails you when certificates are about to expire:
#!/bin/bash
# check-certs.sh — Run from cron daily
DOMAINS="example.com api.example.com mail.example.com"
WARN_DAYS=30
ALERT_EMAIL="admin@example.com"
for domain in $DOMAINS; do
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 "WARNING: Cannot connect to $domain" | \
mail -s "SSL Check Failed: $domain" "$ALERT_EMAIL"
continue
fi
days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
if [ "$days" -lt "$WARN_DAYS" ]; then
echo "Certificate for $domain expires in $days days ($expiry)" | \
mail -s "SSL Expiring: $domain ($days days)" "$ALERT_EMAIL"
fi
done
Add to cron:
0 8 * * * /path/to/check-certs.sh
Monitoring with Prometheus
Use the ssl_exporter or blackbox_exporter to track certificate expiry as a metric.
Blackbox exporter config
# blackbox.yml
modules:
https_check:
prober: http
http:
preferred_ip_protocol: "ip4"
tls_config:
insecure_skip_verify: false
Prometheus scrape config
scrape_configs:
- job_name: 'ssl'
metrics_path: /probe
params:
module: [https_check]
static_configs:
- targets:
- https://example.com
- https://api.example.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115
Grafana alert
Create an alert on probe_ssl_earliest_cert_expiry:
probe_ssl_earliest_cert_expiry - time() < 86400 * 30
This fires when any certificate is within 30 days of expiry.
Auto-Renewal Strategies
Let's Encrypt (certbot)
Certbot handles renewal automatically. Verify it's working:
# Check the renewal timer
systemctl status certbot.timer
# Test renewal
sudo certbot renew --dry-run
ACME for internal certificates
Use an ACME client with your internal CA (like step-ca) for automatic certificate renewal of internal services.
Script-based renewal
For certificates that can't use ACME:
#!/bin/bash
# renew-cert.sh
CERT="/etc/ssl/certs/server.pem"
DAYS_LEFT=$(( ($(date -d "$(openssl x509 -in $CERT -noout -enddate | cut -d= -f2)" +%s) - $(date +%s)) / 86400 ))
if [ "$DAYS_LEFT" -lt 30 ]; then
# Generate new cert (example using getaCert.com API)
curl -X POST https://getacert.com/api/v1/self-signed \
-H "X-API-Key: your-api-key" \
-d '{"cn": "example.com", "days": 365}' \
-o /tmp/new-cert.json
# Extract and install new cert
# ... (parse JSON, write files, reload service)
systemctl reload nginx
echo "Certificate renewed ($DAYS_LEFT days remaining)"
fi
Certificate Lifecycle Best Practices
- Monitor early -- Alert at 30 days before expiry, not 7
- Automate renewal -- Manual renewal fails when people are on vacation
- Test renewal -- Run
certbot renew --dry-runmonthly - Track all certificates -- Maintain an inventory of every certificate, its expiry date, and who owns it
- Use short-lived certificates -- 90-day certs with auto-renewal are more secure than 1-year certs that get forgotten
- Have a runbook -- Document the exact steps to renew each certificate for when automation fails
Next Steps
- Check your domain's certificate expiration
- Set up Let's Encrypt for automatic renewal
- Generate long-term certificates for internal use (up to 10 years)