Intro
TLS certificates are the foundation of secure communication on the internet, yet their underlying architecture often feels opaque to developers and operators. A misconfigured certificate chain, an expired leaf, or a forgotten intermediate can take a service offline instantly. This guide demystifies TLS certificates by connecting architectural concepts to practical commands, expected outputs, and recovery procedures. Whether you manage Nginx, Kubernetes Ingress, or a custom application, you will learn how to inspect, validate, and operate TLS certificates safely.
We will focus on operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify every result, and document recovery paths. Each section includes concrete OpenSSL commands, realistic failure scenarios, and checklists you can adapt to your environment.
Version and Environment Inventory
Before touching any TLS configuration, you need a complete inventory of your environment. Start by identifying:
- Installed OpenSSL version and supported protocols (TLS 1.2, TLS 1.3).
- Deployment topology (load balancers, reverse proxies, backend services).
- Certificate files and their locations.
- Private keys and their access controls.
Run these read-only commands to capture the current state:
openssl version -a
Example output:
OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
built on: Thu Mar 17 14:48:56 2022 UTC
platform: debian-amd64
options: bn(64,64) md2(int) rc4(16x,int) des(int) blowfish(ptr)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -O3 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DNDEBUG
Record the exact version; older versions may not support TLS 1.3 or modern cipher suites.
Next, list all certificate files on the system with their timestamps:
find /etc/ssl /etc/nginx /etc/kubernetes -name '*.pem' -o -name '*.crt' -o -name '*.key' 2>/dev/null | xargs ls -l
This gives you file permissions, ownership, and modification times. Always confirm that private keys are readable only by the service account (e.g., mode 600).
Critical: capture the current state in a timestamped log, including certificate fingerprints and validity periods. Use the following command for each certificate:
openssl x509 -in <certificate.pem> -noout -subject -issuer -serial -dates -fingerprint -sha256
Example output:
subject=C = US, ST = California, L = San Francisco, O = Example Corp, CN = api.example.com
issuer=C = US, O = Let's Encrypt, CN = R3
serial=04C8D2E3A1B4F5
notBefore=Jan 1 00:00:00 2025 GMT
notAfter=Mar 31 23:59:59 2025 GMT
SHA256 Fingerprint=A1:B2:C3:D4:E5:F6:07:08:09:0A:0B:0C:0D:0E:0F:10:11:12:13:14:15:16:17:18:19:1A
Store this output in a secure note system. This baseline becomes invaluable during incident response.
Safe Configuration Path
Changing TLS settings is risky; follow a rigorous process to minimize outages. The core principle: separate observation from intervention. Never mix a state read with a state change.
Read-Only Inspection Commands
Before modifying anything, inspect the current certificate and key:
# View certificate details (without modifying)
openssl x509 -in /etc/nginx/ssl/api.example.com.crt -noout -text | head -30
This shows the full parsed certificate. Check the Subject Alternative Names (SANs):
openssl x509 -in /etc/nginx/ssl/api.example.com.crt -noout -ext subjectAltName
Example output:
X509v3 Subject Alternative Name:
DNS:api.example.com, DNS:api.internal.example.com, IP Address:10.0.0.10
If the hostname you serve is missing, clients will reject the certificate.
Test a remote endpoint without changing anything:
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts < /dev/null
Look for two critical lines in the output:
Verify return code: 0 (ok)
and the negotiated protocol:
Protocol : TLSv1.3
If verification fails, note the error code. A 19 means self-signed certificate in chain, while 20 means unable to get local issuer certificate.
Offline Chain Validation
Before deploying a new certificate, validate the chain offline:
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt -untrusted /etc/nginx/ssl/intermediate.crt /etc/nginx/ssl/api.example.com.crt
Expected success:
api.example.com.crt: OK
If you receive an error like unable to get local issuer certificate, the intermediate certificate is missing or not trusted. Include the full chain (leaf + intermediates) in your server configuration, but never include the root; clients already have it.
Minimal Change and Verification
When you must change something, make one small, reversible edit. For example, if updating Nginx's certificate path:
Edit /etc/nginx/sites-available/api.example.com:
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/api.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
}
After the change, test the configuration before reloading:
nginx -t
Expected output:
nginx: configuration file /etc/nginx/nginx.conf test is successful
Then reload, and immediately verify with s_client and your baseline fingerprint comparison. Keep a backup of the previous certificate and key to enable rollback.
Verification and Diagnostics
This section gives you a systematic approach to diagnose TLS issues. Treat every diagnostic as an experiment with a clear expected result.
Step 1: Verify Certificate Files
Run the openssl x509 command from the inventory section on each certificate. Check:
notBeforeandnotAfter: ensure current time is within range.subjectandissuer: confirm the intended CA.X509v3 Key UsageandExtended Key Usage: for server certificates, should includeDigital Signature, Key EnciphermentandTLS Web Server Authentication.
Use openssl x509 -purpose to get a quick assessment:
openssl x509 -in api.example.com.crt -noout -purpose
If it reports Certificate purposes: SSL server : Yes, you are good.
Step 2: Verify Private Key Match
Ensure the public key in the certificate matches the private key:
openssl x509 -in api.example.com.crt -noout -pubkey > cert.pub
openssl pkey -in api.example.com.key -pubout > key.pub
diff cert.pub key.pub
If diff produces no output, they match. If not, you have a mismatched pair, and clients will see handshake failures.
Step 3: Test the Live Endpoint
Use openssl s_client with debug output to see handshake details:
openssl s_client -connect api.example.com:443 -servername api.example.com -state -msg
The -state flag prints handshake states; -msg shows protocol messages. Look for anomalies like alert certificate expired.
To test a specific TLS version:
openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2
If you want to check that TLS 1.0 is disabled (it should be), use -no_tls1 or specify -tls1 and expect failure.
Step 4: Examine the Chain Presented by the Server
In the s_client output, find the Certificate chain section:
Certificate chain
0 s:C = US, ST = California, L = San Francisco, O = Example Corp, CN = api.example.com
i:C = US, O = Let's Encrypt, CN = R3
1 s:C = US, O = Let's Encrypt, CN = R3
i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
If the server does not send intermediate certificate 1, many clients (especially mobile) will fail to build the chain. Ensure your server configuration includes the full chain file.
Step 5: Check Certificate Revocation (Optional)
If your environment uses OCSP, query the responder:
openssl ocsp -issuer intermediate.crt -cert api.example.com.crt -url http://r3.o.lencr.org -text
Look for Response verify OK and Cert Status: good.
Failure Modes and Recovery
TLS failures often show cryptic error messages. Here are common scenarios, their causes, and recovery steps.
Expired Certificate
Symptoms: Browsers show NET::ERR_CERT_DATE_INVALID, s_client returns alert certificate expired, or your monitoring alerts on notAfter date.
Diagnosis:
openssl x509 -in api.example.com.crt -noout -dates
Output shows:
notBefore=Jan 1 00:00:00 2025 GMT
notAfter=Mar 31 23:59:59 2025 GMT
Current date is after Mar 31. Recovery: Obtain a new certificate from your CA, install it, and reload the service. Immediately verify with s_client. To prevent recurrence, set up automated renewal (e.g., certbot with systemd timer) and monitor expiry with a service like UptimeRobot or a custom script.
Mismatched Hostname
Symptoms: Browsers show ERR_CERT_COMMON_NAME_INVALID, and s_client returns alert bad certificate.
Diagnosis:
openssl s_client -connect api.example.com:443 -servername api.example.com < /dev/null | openssl x509 -noout -ext subjectAltName
Output might show:
X509v3 Subject Alternative Name:
DNS:api.olddomain.com
Your client expects api.example.com. Recovery: Request a new certificate with correct SANs, or if using a wildcard, ensure pattern matches (e.g., *.example.com covers api.example.com but not api.internal.example.com).
Incomplete Chain
Symptoms: Some clients connect fine (e.g., desktop browsers) while others fail (mobile apps, APIs). openssl s_client may return verify error:num=20:unable to get local issuer certificate.
Diagnosis: Check the chain sent by the server:
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts < /dev/null
Count the certificates under Certificate chain. If only one certificate is shown (the leaf), the intermediate is missing. Recovery: Concatenate the leaf and intermediate into a full chain file:
cat api.example.com.crt intermediate.crt > fullchain.crt
Point your server's ssl_certificate directive to fullchain.crt and reload.
Private Key Permission Denied
Symptoms: Service fails to start with error like cannot load certificate key.
Diagnosis:
ls -l /etc/nginx/ssl/api.example.com.key
If permissions are too restrictive (e.g., -rw------- owned by root) but Nginx runs as www-data, it cannot read. Recovery: Set appropriate ownership and permissions, e.g., chown root:www-data and chmod 640. Then restart the service.
Protocol or Cipher Mismatch
Symptoms: Handshake fails with no protocols available or sslv3 alert handshake failure.
Diagnosis: Use s_client to test specific protocol versions:
openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_3
If this fails but TLS 1.2 works, the server may not support TLS 1.3. Recovery: Review your server's ssl_protocols and ssl_ciphers settings. For broad compatibility, use TLSv1.2 TLSv1.3 and a modern cipher list. Then reload and retest.
Rollback Plan
Always have a rollback plan before deploying certificate changes. Keep a backup of the previous certificate and key files, along with a copy of the working configuration. For Nginx, you can use a timestamped backup:
cp api.example.com.crt api.example.com.crt.backup-$(date +%Y%m%d)
cp api.example.com.key api.example.com.key.backup-$(date +%Y%m%d)
cp /etc/nginx/sites-available/api.example.com /etc/nginx/sites-available/api.example.com.backup-$(date +%Y%m%d)
If the new configuration fails, restore the backups and reload:
cp api.example.com.crt.backup-20250101 api.example.com.crt
cp api.example.com.key.backup-20250101 api.example.com.key
cp /etc/nginx/sites-available/api.example.com.backup-20250101 /etc/nginx/sites-available/api.example.com
nginx -s reload
Operations Checklist
Use this checklist for routine certificate operations, and assign a single accountable owner for each item. Decide on a review frequency: weekly for high-traffic services, monthly for internal services, or quarterly for long-lived certificates. The owner should verify each item on the schedule and document the result.
| Task | Owner | Frequency | Command or Check | Expected Result |
|---|---|---|---|---|
| Verify certificate expiration | Priya Shah, Engineering Lead | Weekly | openssl x509 -in cert.pem -noout -dates and compare to date | notAfter at least 30 days in future |
| Check certificate chain completeness | Marcus Chen, DevOps Engineer | Monthly | openssl s_client -showcerts and count chain entries | At least 2 certificates (leaf + intermediate) |
| Test private key match | Priya Shah, Engineering Lead | Quarterly | openssl x509 -pubkey vs openssl pkey -pubout diff | No diff output |
| Validate hostname coverage | Marcus Chen, DevOps Engineer | Quarterly | openssl x509 -ext subjectAltName | All serving domains listed |
| Review protocol support | Priya Shah, Engineering Lead | Monthly | openssl s_client -tls1_3 and -tls1_2 | Both connect successfully |
| Check for revoked certificates | Marcus Chen, DevOps Engineer | Monthly | openssl ocsp -issuer ... -cert ... | Cert Status: good |
| Test renewal process in staging | Priya Shah, Engineering Lead | Before each renewal | Dry-run certbot or manual ACME client | Renewal completes without errors |
| Verify backup and rollback plan | Marcus Chen, DevOps Engineer | Semi-annually | Restore from backup in test environment | Service returns to working state |
Store this checklist in a shared document or ticketing system. After each check, the owner records the result and any deviations. If a deviation occurs, treat it as an incident and follow the failure recovery procedures.
Common Pitfalls and How to Avoid Them
Understanding common mistakes can save hours of debugging. Here are five frequent pitfalls, why they happen, and how to prevent or recover from them.
1. Ignoring Certificate Expiry Until It's Too Late
Why it happens: Certificates have long validity (up to 1 year), so teams forget. No monitoring is in place.
How to avoid: Set up automated monitoring with alerts at 30, 14, and 7 days before expiry. Use a monitoring tool or a simple cron job:
# Check all certs in /etc/nginx/ssl and warn if expiring within 30 days
find /etc/nginx/ssl -name '*.crt' -exec openssl x509 -in {} -noout -checkend 2592000 \; -print
If the output is empty, all certificates expire in more than 30 days.
How to recover: Have a tested renewal process. Automate with ACME clients (certbot, lego) that run periodically.
2. Using a Wildcard Certificate Where It Doesn't Fit
Why it happens: Wildcards like *.example.com are convenient but do not cover multi-level subdomains (a.b.example.com) or different root domains (example.net).
How to avoid: Before acquiring, list all hostnames you need to cover. Use a SAN certificate if multiple exact names are needed. Verify with openssl x509 -ext subjectAltName.
How to recover: If you discover coverage gaps, obtain a new certificate with correct SANs and redeploy.
3. Mixing Up Private Key and Certificate Files
Why it happens: Similar file names and extensions (.pem, .key, .crt) lead to confusion.
How to avoid: Maintain a clear naming convention: service.example.com.key, service.example.com.crt, service.example.com.fullchain.crt. Store them in separate directories if needed. Use the key match check regularly.
How to recover: If you accidentally overwrite a file, restore from backup. If no backup, you may need to generate a new key and certificate pair (and revoke the old one).
4. Over-Permissive Private Key Permissions
Why it happens: For convenience, developers set keys to mode 644 or 777.
How to avoid: Always set private keys to 600 (or 640 with group access for service). Audit permissions regularly:
find /etc/ssl /etc/nginx -name '*.key' -exec ls -l {} \;
How to recover: If a key was exposed, treat it as compromised: revoke the corresponding certificate, generate a new key and certificate, and update all references.
5. Hardcoding TLS Configuration Values Without Version Control
Why it happens: Quick fixes are made directly on servers; changes are not tracked.
How to avoid: Store all TLS-related configuration (Nginx, HAProxy, Kubernetes ingress) in a version control system (Git). Use configuration management (Ansible, Terraform) to deploy.
How to recover: If a server configuration is lost, you can redeploy from the repository. Ensure secrets (private keys) are not committed; use a secrets manager.
Conclusion
TLS certificate architecture is not just about encryption; it is about operational discipline. By following the practices in this guide—observing before changing, validating chains offline, testing endpoints, maintaining checklists, and preparing rollback plans—you can prevent most certificate-related outages.
Start with a low-risk task: run the inventory commands on one of your production services. Record the current certificate details, check the expiration date, and verify the chain. Then, review your monitoring for certificate expiry and ensure you have automated alerts. Assign an owner to the weekly certificate check and document the procedure.
A reliable TLS workflow makes failures visible, protects sensitive keys, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. With these tools, you can keep your services secure and available.