Setting Up SSL Certificate Monitoring (Expiry Date)
Expired SSL certificate makes site inaccessible to all users. Browsers show warning page, SEO rankings drop, user trust lost. Certificate expiry monitoring is one of simplest tasks with high impact on availability.
What to Monitor
- Main domain certificate expiry
- Subdomain certificates (each separately — wildcard
*.example.comnot always used) - Intermediate certificates in chain (CA chain)
- Internal service certificates (API, internal domains)
Prometheus Blackbox Exporter
# blackbox.yml
modules:
https_check:
prober: http
timeout: 15s
http:
valid_status_codes: [] # Any status — check only SSL
method: HEAD
tls_config:
insecure_skip_verify: false
fail_if_ssl: false
fail_if_not_ssl: true
# prometheus.yml scrape config
scrape_configs:
- job_name: 'ssl_certificate_check'
metrics_path: /probe
params:
module: [https_check]
static_configs:
- targets:
- https://example.com
- https://api.example.com
- https://admin.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
Metric probe_ssl_earliest_cert_expiry contains certificate expiry timestamp.
Prometheus alert:
- alert: SSLCertificateExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 30 * 24 * 3600
labels:
severity: warning
annotations:
summary: "SSL cert on {{ $labels.instance }} expires in {{ $value | humanizeDuration }}"
- alert: SSLCertificateExpiryCritical
expr: probe_ssl_earliest_cert_expiry - time() < 7 * 24 * 3600
labels:
severity: critical
annotations:
summary: "SSL cert on {{ $labels.instance }} expires in {{ $value | humanizeDuration }}!"
Python Script for Monitoring
import ssl
import socket
from datetime import datetime, timezone
def check_ssl_expiry(hostname: str, port: int = 443) -> dict:
context = ssl.create_default_context()
with socket.create_connection((hostname, port), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
expiry_str = cert['notAfter']
expiry_date = datetime.strptime(expiry_str, '%b %d %H:%M:%S %Y %Z')
expiry_date = expiry_date.replace(tzinfo=timezone.utc)
days_remaining = (expiry_date - datetime.now(timezone.utc)).days
return {
'hostname': hostname,
'expires_at': expiry_date.isoformat(),
'days_remaining': days_remaining,
'issuer': dict(x[0] for x in cert['issuer']),
'subject': dict(x[0] for x in cert['subject'])
}
Automatic Renewal with Let's Encrypt
If using Let's Encrypt + Certbot or ACME client, manual renewal not needed. But monitoring still necessary — automation sometimes breaks.
# Check certbot timer status
systemctl status certbot.timer
# Test run without actual update
certbot renew --dry-run
Certbot renews certificates when < 30 days remain. Monitoring triggers at < 30 days = automation didn't work in time.
Uptime Robot / Better Uptime
Fastest option — external SSL monitoring services:
- Uptime Robot (free plan): SSL expiry check, notification at 30/7/1 day
- Better Uptime, StatusCake: similar
Setup: 5 minutes. Complements Prometheus monitoring.







