Intro
Certificate expiry and misconfiguration are among the most common causes of avoidable outages. A certificate that expires at 3 a.m. can take down a production API, a checkout flow, or an entire customer portal. Monitoring TLS certificates and alerting on their health is not optional for teams that value uptime.
This guide is for developers, DevOps engineers, SREs, and technical founders who need a practical way to monitor certificates, set meaningful alerts, and respond to incidents without over-engineering. We'll cover how to inspect certificates with OpenSSL, what metrics to track, how to build a simple dashboard, and what to do when something goes wrong.
Every command in this article is safe to run against your own systems, and we'll emphasize read-only checks, minimal changes, and recovery paths. By the end, you'll have a clear checklist to implement or improve TLS monitoring in your environment.
Version and Environment Inventory
Before troubleshooting or monitoring, you need to know what you're dealing with. This means capturing the current state of your certificates, the software versions, and the endpoints involved. A common mistake is to run a command that assumes a particular OpenSSL version or certificate format. Always check the basics first.
Check OpenSSL Version
OpenSSL is the de facto tool for inspecting TLS certificates, and its behavior changes between versions. Run:
openssl version
Expected output looks like:
OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
If you're on a system with OpenSSL 1.1.1, some flags like -brief may not be available. Always verify the version before scripting.
List Certificates on a Server
On a typical Linux server, certificates are often stored under /etc/ssl or /etc/pki. You can list them with:
find /etc/ssl -name "*.pem" -o -name "*.crt" 2>/dev/null
This helps you locate the certificate files you need to inspect. Note the paths; you'll use them in the next commands.
Identify Certificate Subject and Expiry
To inspect a certificate file without modifying it, use:
openssl x509 -in /etc/ssl/certs/example.com.pem -noout -subject -issuer -dates -fingerprint -sha256
Sample output:
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R3
notBefore=Jun 1 00:00:00 2024 GMT
notAfter=Aug 30 00:00:00 2024 GMT
SHA256 Fingerprint=3A:4B:5C:... (full hash)
Record these values. They become your baseline for comparison after any change.
Test a Remote Endpoint
For a live server, use openssl s_client:
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl x509 -noout -dates -subject
Expected output:
subject=CN = example.com
notBefore=Jun 1 00:00:00 2024 GMT
notAfter=Aug 30 00:00:00 2024 GMT
If the connection fails, check firewall rules or whether the service is listening on port 443.
Safe Configuration Path
Monitoring should never risk breaking production. The safe path involves:
- Read-only observation: commands that do not change state.
- Non-destructive validation: testing a configuration without applying it.
- Scoped changes: applying only what is necessary, with rollback options.
- Verification after change: confirming the intended result with a check.
Validate a Certificate Chain Offline
Before deploying a new certificate, validate the chain with:
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt -untrusted /etc/ssl/certs/intermediate.pem /etc/ssl/certs/example.com.pem
Expected output on success:
/etc/ssl/certs/example.com.pem: OK
If the chain is incomplete, you'll see an error like error 20 at 0 depth lookup: unable to get local issuer certificate. Fix the chain before applying.
Test a Web Server Configuration Without Restarting
If using Nginx, check the configuration with:
nginx -t
Expected output:
nginx: configuration file /etc/nginx/nginx.conf test is successful
Only after this passes should you reload:
systemctl reload nginx
Then verify the certificate is served:
echo | openssl s_client -connect localhost:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
Automating Renewal Safely
If you use Let's Encrypt with Certbot, perform a dry run first:
certbot renew --dry-run
This tests renewal without actually issuing certificates. Then schedule the real renewal with cron or systemd timer.
Verification and Diagnostics
Once a certificate is in place or when you suspect an issue, run a systematic set of verifications.
Check Expiry Date Remotely
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate
Expected output:
notAfter=Aug 30 00:00:00 2024 GMT
Calculate the remaining days with a simple script:
expiry_date=$(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_date" +%s)
current_epoch=$(date +%s)
days_left=$(( (expiry_epoch - current_epoch) / 86400 ))
echo "$days_left days left"
If the number is less than 30, you should be alerting.
Verify Hostname Match
A certificate is valid only if its subject or SAN matches the requested hostname. Check with:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
Expected output:
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com
If the hostname you're connecting to is not listed, clients will get a certificate error.
Check Certificate Chain Presented by Server
Ensure the server sends the full chain (leaf + intermediate):
echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep "s:"
You should see at least two certificates: the server cert and the intermediate. Missing intermediates cause trust errors on some clients.
Test TLS Protocol and Cipher
Confirm the server supports TLS 1.2 or 1.3 and strong ciphers:
echo | openssl s_client -connect example.com:443 -tls1_2 2>/dev/null | grep "Protocol\|Cipher"
For TLS 1.3:
echo | openssl s_client -connect example.com:443 -tls1_3 2>/dev/null | grep "Protocol\|Cipher"
If TLS 1.2 fails but 1.3 works, you may have a compatibility issue for older clients.
Failure Modes and Recovery
Certificates fail in predictable ways. Understanding these modes helps you respond quickly.
Expired Certificate
Symptoms: Browsers show "Your connection is not private", APIs return SSL errors, monitoring alerts fire.
Diagnosis: Check the expiry date as shown above. If notAfter is in the past, the certificate is expired.
Recovery:
- Obtain a new certificate from your CA.
- Replace the certificate file on the server.
- Reload the web server:
systemctl reload nginx
- Verify with
openssl s_clientthat the new expiry date is in the future.
Prevention: Set alerts at 30, 14, and 7 days before expiry. Automate renewal where possible.
Hostname Mismatch
Symptoms: Certificate valid but browser complains about name mismatch.
Diagnosis: Compare the requested hostname with the certificate SAN list.
Recovery:
- If using a wildcard certificate, ensure the wildcard covers the subdomain (e.g.,
*.example.comcoversapi.example.com). - If the certificate is for the wrong domain, issue a new one with correct SANs.
- Update the server configuration to use the correct certificate.
Incomplete Chain
Symptoms: Some clients (especially mobile) fail to verify, while desktop browsers may work because they cache intermediates.
Diagnosis: Use openssl s_client -showcerts and check that the intermediate is present. Alternatively, use an online SSL checker like SSL Labs.
Recovery:
- Concatenate leaf and intermediate certificates in the correct order (leaf first, then intermediate).
- For Nginx, set
ssl_certificateto the full chain file. - Reload and verify.
Revoked Certificate
Symptoms: Certificate is within validity but rejected by clients.
Diagnosis: Check revocation status with OCSP:
openssl ocsp -issuer intermediate.pem -cert example.com.pem -url http://ocsp.example.com -text
Look for Cert Status: revoked or good.
Recovery: If revoked, immediately replace the certificate. Investigate why it was revoked (compromised key, mis-issuance).
Private Key Mismatch
Symptoms: Web server fails to start or TLS handshake fails.
Diagnosis: Compare the modulus of the certificate and private key:
openssl x509 -noout -modulus -in example.com.pem | openssl md5
openssl rsa -noout -modulus -in example.com.key | openssl md5
If the MD5 hashes differ, they do not match.
Recovery: Obtain the correct key or generate a new key and certificate pair.
Operations Checklist
Here is a concrete checklist for ongoing TLS certificate operations. Assign a single owner for each item and review quarterly or more frequently.
| # | Task | Command / Tool | Frequency | Owner | Expected Result |
|---|
| 1 | Check expiry of all public certs | echo | openssl s_client -connect :443 2>/dev/null | openssl x509 -noout -enddate |
| Daily (automated) | Priya Shah, DevOps Engineer | > 30 days remaining |
| 2 | Verify SAN matches hostnames | openssl x509 -in cert.pem -noout -text | grep -A1 SAN |
| On deployment | Marcus Lee, Backend Lead | All hostnames listed |
| 3 | Test certificate chain completeness | openssl s_client -showcerts |
| Weekly | Priya Shah | At least leaf + intermediate |
| 4 | Check OCSP revocation status | openssl ocsp -issuer intermediate.pem -cert leaf.pem -url http://ocsp.example.com |
| Monthly | Priya Shah | Status: good |
| 5 | Validate private key matches cert | openssl x509 -noout -modulus -in cert.pem | openssl md5 and openssl rsa -noout -modulus -in key.pem | openssl md5 |
| On key rotation | Marcus Lee | MD5 hashes match |
| 6 | Test renewal process (dry run) | certbot renew --dry-run |
| Monthly | Priya Shah | No errors |
| 7 | Monitor TLS protocol version | openssl s_client -tls1_2 -connect host:443 and -tls1_3 |
| Quarterly | Security Team | TLS 1.2 and 1.3 enabled |
| 8 | Review monitoring alerts configuration | Check alert rules in Prometheus/Alertmanager |
| Quarterly | Priya Shah | Alerts triggered on test |
| 9 | Update inventory of certificates and owners | Maintain a spreadsheet or CMDB |
| Monthly | Marcus Lee | All certs documented |
| 10 | Run incident response drill for certificate expiry | Simulate an expired certificate in staging |
| Annually | Priya Shah | Recovery in under 30 min |
Common Pitfalls and How to Avoid Them
- Not setting alerts: Teams often rely on manual checks or memory. Expiry at odd hours goes unnoticed. How to avoid: Implement automated checks with Prometheus Blackbox Exporter or a simple cron job that pushes to a notification channel.
- Testing only in production: Making changes directly on production without testing in staging leads to downtime. How to avoid: Mirror your TLS configuration in a staging environment and test there first.
- Hardcoding certificate paths in scripts: Paths change between servers and environments. How to avoid: Use variables or configuration management (Ansible, Puppet) to manage paths.
- Ignoring intermediate certificates: New developers may upload only the leaf certificate, breaking chain validation. How to avoid: Include the full chain file provided by your CA and verify with
openssl verify. - Not monitoring revocation: A certificate can be valid but revoked. How to avoid: Periodically check OCSP status and configure OCSP stapling on your server.
- Assuming all clients support the latest TLS: Disabling TLS 1.0/1.1 might break legacy clients. How to avoid: Analyze client logs before disabling older protocols, and offer a gradual deprecation plan.
- Not testing renewal process: Automated renewal may fail silently due to changed DNS or firewall rules. How to avoid: Run
certbot renew --dry-runmonthly and monitor the actual renewal logs. - Storing private keys in version control: Keys in git repositories are a security risk. How to avoid: Use secret management tools like HashiCorp Vault or environment variables.
Conclusion
TLS certificate monitoring and alerting is a critical operational practice. By following the steps in this guide, you can:
- Identify the current state of certificates and endpoints.
- Safely inspect and validate certificates without causing disruption.
- Diagnose common failure modes and recover quickly.
- Establish a routine checklist with clear ownership and frequency.
Start with one low-risk verification: run the expiry check on your most important endpoint, set up a cron job to alert you at 30 days, and document the recovery steps. Expand from there. The goal is to make certificate failures visible before they become incidents.
Remember, a reliable TLS monitoring workflow protects your users, your reputation, and your sleep.