Introduction
Handshake failures are one of the most common pain points when working with secure services. Whether you're a backend developer, a site reliability engineer, or a DevOps specialist, understanding why a TLS/SSL handshake breaks and how to fix it can save hours of debugging time.
In this article we’ll walk through the most frequent causes, provide step‑by‑step troubleshooting commands, and share ready‑to‑run snippets that you can drop into your CI/CD pipelines.
1. Typical Reasons for a Handshake Failure
| Symptom | Likely Cause |
|---|---|
SSL handshake failed (curl) |
Expired/invalid server cert, missing intermediate CA |
certificate verify failed (Python) |
Wrong trust store or hostname mismatch |
TLSV1_ALERT_PROTOCOL_VERSION |
Client uses deprecated TLS version |
ERR_CERT_COMMON_NAME_INVALID (browser) |
Hostname does not match the CN/SAN |
1.1 Expired or Self‑Signed Certificates
Most clouds rotate certificates automatically, but on‑prem services often rely on manually‑managed certs. An expired cert will cause every client that validates the chain to abort the handshake.
1.2 Missing Intermediate Certificates
If the server presents only the leaf cert, clients that don’t have the intermediate cached will fail the verification step.
1.3 Protocol Mismatch
Older libraries may still default to TLS 1.0/1.1, while modern servers require TLS 1.2+.
2. Quick Verification Commands
# Show the full certificate chain as seen by OpenSSL
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts
# Curl with verbose output – useful for HTTP APIs
curl -v https://api.example.com
# Python requests – force verification to see the exact error
python - <<'PY'
import requests, urllib3
urllib3.disable_warnings()
try:
r = requests.get('https://api.example.com', timeout=5)
print('Status:', r.status_code)
except requests.exceptions.SSLError as e:
print('SSL error:', e)
PY
These commands reveal whether the server is sending the full chain, which TLS versions are negotiated, and the exact verification error.
3. Step‑by‑Step Troubleshooting
Step 1 – Check the Certificate Dates
openssl x509 -noout -dates -in /path/to/leaf.crt
If the notAfter date is in the past, renew the cert.
Step 2 – Validate the Chain Locally
openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt leaf.crt
A error 20 at 0 depth lookup:unable to get local issuer certificate means the intermediate is missing.
Step 3 – Add Missing Intermediates
Create a bundle:
cat leaf.crt intermediate.crt > fullchain.pem
Configure your server (NGINX, Apache, HAProxy) to use fullchain.pem instead of just leaf.crt.
Step 4 – Enforce Modern TLS Versions
For NGINX:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
For Java (system property):
-Djdk.tls.client.protocols=TLSv1.2,TLSv1.3
Step 5 – Update the Trust Store
On Linux you can refresh the CA bundle:
sudo update-ca-certificates
On Windows, import the missing root/intermediate via the MMC snap‑in.
4. Automating the Fix
If you often run into missing intermediates, the following Bash helper bundles the leaf with the correct chain and reloads the service:
#!/usr/bin/env bash
set -euo pipefail
LEAF=$1
INTERMEDIATE_URL="https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem"
INTERMEDIATE=$(mktemp)
curl -sSL "$INTERMEDIATE_URL" -o "$INTERMEDIATE"
cat "$LEAF" "$INTERMEDIATE" > /etc/nginx/ssl/fullchain.pem
systemctl reload nginx
rm -f "$INTERMEDIATE"
You can download the pre‑configured script here: Download the pre‑configured script here.
5. When All Else Fails – Use a Diagnostic Container
Running the same checks inside a minimal container guarantees a clean environment:
FROM alpine:latest
RUN apk add --no-cache openssl curl python3 py3-pip && pip install requests
COPY test.sh /test.sh
CMD ["/bin/sh","/test.sh"]
Build and run:
docker build -t tls‑debug .
docker run --rm tls‑debug
The output mirrors the host commands but eliminates local CA cache issues.
Conclusion
SSL/TLS handshake failures are rarely mystical – they are usually the result of an expired cert, a missing intermediate, or a protocol mismatch. By systematically verifying the chain, updating trust stores, and enforcing modern TLS versions you can resolve the majority of issues in minutes.
Ready to automate the remediation? Get the complete patch tool: Get the complete patch tool.
For a deeper dive into certificate lifecycle management, check out the full repository fix: Access the full repository fix.
Top comments (0)