Introduction
We've all heard the story: a production certificate silently expires at 2 AM on a Friday, customers can't reach your platform, and the on-call engineer spends the weekend fighting fires. Monitoring SSL certificate expiry: a step-by-step guide will show you how to catch these disasters weeks before they happen. With Prometheus and Grafana, you can automatically check every domain and subdomain, alert your team with plenty of lead time, and sleep soundly knowing your certificates are under constant watch.
Why certificate monitoring matters
SSL certificates don't fail gracefully. When they expire, browsers show security warnings, APIs refuse connections, and your users experience hard failures with no fallback. Manual renewal calendars are unreliable—people go on vacation, change jobs, or simply forget. Even certificate authorities that send reminder emails can't guarantee you'll see them in time to coordinate a renewal and deployment.
The stakes are higher when you run multiple environments and services. A staging certificate expiring won't wake anyone up, but it will silently break your integration tests. A subdomain certificate that you forgot existed will expire and take down a specific feature or partner integration without affecting your main site, making it hard to debug.
Blackbox exporter TLS metrics
The Prometheus blackbox exporter is purpose-built to probe endpoints and extract metrics. When configured to check TLS certificates, it connects to your domain, validates the certificate chain, and exposes two critical metrics:
probe_ssl_earliest_cert_expiry— Unix timestamp of the soonest expiring certificate in the chainprobe_ssl_last_chain_expiry_timestamp_seconds— timestamp of the final certificate in the chain
The first metric is what matters most: it catches expiry of the leaf certificate, intermediate certificates, and any other cert in the presented chain.
Start by installing the blackbox exporter. On most systems, you can use your package manager or pull the binary directly:
curl -L https://github.com/prometheus/blackbox_exporter/releases/download/v0.24.0/blackbox_exporter-0.24.0.linux-amd64.tar.gz | tar xz
cd blackbox_exporter-0.24.0.linux-amd64
./blackbox_exporter --config.file=blackbox.ymlNext, configure it to probe HTTPS endpoints. Create or update your blackbox.yml:
modules:
https_2xx:
prober: http
timeout: 5s
http:
valid_http_versions:
- HTTP/1.1
- HTTP/2.0
valid_status_codes:
- 200
- 301
- 302
no_follow_redirects: false
https_cert:
prober: http
timeout: 5s
http:
valid_http_versions:
- HTTP/1.1
no_follow_redirects: true
tls_config:
insecure_skip_verify: falseThe https_cert module focuses purely on TLS validation without requiring a specific HTTP response code. This is ideal for checking certificate validity regardless of what your endpoint returns.
Configure Prometheus to scrape the blackbox exporter for each domain you want to monitor. In your prometheus.yml:
scrape_configs:
- job_name: 'blackbox-https'
metrics_path: /probe
static_configs:
- targets:
- https://api.example.com
- https://app.example.com
- https://admin.example.com
- https://partner-api.example.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: localhost:9115Run this for five minutes and you'll see probe_ssl_earliest_cert_expiry appear in Prometheus. Graph it in Grafana to visualize when each certificate expires.
Alert 21 days before expiry
A 21-day alert window gives you time to renew, test, and deploy without panic. It also catches cases where renewal automation fails silently—your monitoring catches it before your users do.
Add this alert rule to your Prometheus configuration. We'll use the probe_ssl_earliest_cert_expiry metric and calculate how many days until expiry:
groups:
- name: ssl_certificates
interval: 300s
rules:
- alert: SSLCertificateExpiringSoon
expr: |
(probe_ssl_earliest_cert_expiry - time()) < 21 * 24 * 3600
for: 1h
labels:
severity: warning
team: platform
annotations:
summary: "SSL certificate for {{ $labels.instance }} expires in less than 21 days"
description: "Certificate for {{ $labels.instance }} will expire on {{ humanize (probe_ssl_earliest_cert_expiry{instance=\"{{ $labels.instance }}\"}) }}. Renew it now."
- alert: SSLCertificateExpiryCritical
expr: |
(probe_ssl_earliest_cert_expiry - time()) < 7 * 24 * 3600
for: 15m
labels:
severity: critical
team: platform
annotations:
summary: "SSL certificate for {{ $labels.instance }} expires in less than 7 days"
description: "Certificate for {{ $labels.instance }} is expiring very soon. Renew immediately."
- alert: SSLCertificateExpired
expr: |
probe_ssl_earliest_cert_expiry - time() < 0
for: 5m
labels:
severity: critical
team: platform
annotations:
summary: "SSL certificate for {{ $labels.instance }} has EXPIRED"
description: "Certificate for {{ $labels.instance }} expired at {{ humanize (probe_ssl_earliest_cert_expiry{instance=\"{{ $labels.instance }}\"}) }}. This is a production incident."The three tiers—21 days, 7 days, and expired—let you escalate response proportionally. At 21 days, send it to your team Slack channel. At 7 days, page the on-call engineer. If it's already expired, it's a SEV-1 incident.
Configure your alertmanager to route these alerts where your team actually watches:
route:
receiver: 'platform-team'
routes:
- match:
alertname: SSLCertificate.*
severity: warning
receiver: slack-warnings
group_wait: 10m
- match:
alertname: SSLCertificate.*
severity: critical
receiver: pagerduty
group_wait: 1mCover every domain and subdomain
The most common mistake is assuming your monitoring covers everything. You monitor api.example.com and app.example.com, but miss internal.example.com, staging-api.example.com, or an old subdomain that's still being used by a legacy partner.
Start by auditing all domains and subdomains actually serving traffic. Check your infrastructure-as-code repositories, your CDN configuration, your load balancer rules, and your DNS records. Ask your DevOps and security teams if they know of any endpoints you missed. Document them in a spreadsheet or config file:
domains:
production:
- https://example.com
- https://www.example.com
- https://api.example.com
- https://app.example.com
- https://websocket.example.com
staging:
- https://staging.example.com
- https://staging-api.example.com
internal:
- https://admin.example.com
- https://metrics.example.com
- https://internal-api.example.com
legacy:
- https://partner-old.example.com
- https://legacy.example.comAdd all of these to your Prometheus blackbox scrape config. If you have dozens of subdomains, use a script or dynamic discovery to generate the target list. Some teams use consul or file-based discovery to automatically pick up new endpoints.
Set a quarterly reminder to re-audit your domains. Endpoints get created, retired, and forgotten. A cert that nobody is monitoring will still expire and cause an outage.
Check intermediate chains too
Most certificate chains include at least one intermediate certificate between your leaf certificate and the root. If an intermediate certificate in your chain expires, your certificate becomes invalid even if the leaf cert is fresh.
The blackbox exporter's probe_ssl_earliest_cert_expiry metric already checks the entire chain and returns the earliest expiry date. This is exactly what you want—it catches expiring intermediates automatically.
However, there's a catch: some CAs rotate intermediate certificates on a predictable schedule, and if your server isn't configured to serve the latest intermediate in its chain, clients may reject your certificate. Validate that your web server is actually sending the complete chain.
Test this manually for each domain:
openssl s_client -connect api.example.com:443 -showcertsLook for "chain depth" output. You should see the leaf certificate, one or more intermediate certificates, and ultimately a self-signed root. If you only see the leaf certificate, your web server configuration may be incomplete. Some certificate providers include setup guides with the exact intermediate bundle to serve.
In production, misconfigured chains are often invisible until the old intermediate expires. Your users will see "certificate not trusted" errors, and some clients (particularly older mobile apps) may fail silently. By monitoring the entire chain, you catch this months in advance.
Common mistakes
- Not monitoring staging and internal domains. A staging certificate that expires won't page anyone, but it will break your CI/CD pipeline and integration tests. Include every endpoint in your monitoring, not just production-facing services.
- Alerting too late or too early. Alerting at 1 day before expiry is too aggressive and noisy; alerting at 1 day before it expires is too late to coordinate safely. 21 days is the sweet spot for most teams.
- Assuming your web server is sending the full chain. Just because a certificate is valid doesn't mean your web server is configured to serve the full chain. Clients may reject it. Periodically verify with
openssl s_client. - Forgetting about wildcard and multi-domain certificates. A wildcard cert covering
*.example.comis still one certificate. It expires as a unit. Make sure you're monitoring the primary domain it's issued to, not just a few subdomains under it. - Not handling certificate rotation in your automation. Even if you set up perfect monitoring and alerts, if your certificate renewal and deployment pipeline doesn't work, you're just getting paged more often. Test your renewal process quarterly.
Key takeaways
- Use the Prometheus blackbox exporter to automatically check TLS certificate expiry for all your domains and subdomains.
- Alert at 21 days before expiry (warning), 7 days before (critical), and immediately if expired.
- Audit and document every domain serving traffic, including staging, internal, and legacy subdomains—missing one will eventually bite you.
- The blackbox exporter checks the entire certificate chain, catching expiring intermediates automatically.
- Periodically verify that your web servers are actually sending the complete certificate chain, not just the leaf certificate.
- Pair monitoring with an automated renewal and deployment pipeline so alerts lead to action, not just noise.






Comments
Loading comments…