Introduction
A TLS handshake failure is one of the most frustrating errors you can encounter when deploying web services. Whether you see SSL handshake failed, certificate verify failed, or a generic ERR_SSL_PROTOCOL_ERROR, the underlying cause is usually a mis‑configuration of certificates, protocol versions, or trust stores. This guide walks you through the most common culprits and provides concrete, reproducible steps to get your connections working again.
Common Causes
- Expired or revoked certificate – The leaf certificate is no longer valid.
- Incomplete certificate chain – Intermediate certificates are missing on the server.
-
Hostname mismatch – The
CN/SANdoes not match the request host. - Unsupported TLS version or cipher suite – Client and server cannot agree on a common set.
- Out‑of‑date trust store – The client does not trust the issuing CA.
Step‑by‑Step Troubleshooting
1. Inspect the Server Certificate Chain
# Replace example.com with your host
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | openssl x509 -noout -text
Look for the Certificate chain section. All intermediate certificates must be presented by the server. If any are missing, add them to your server config (e.g., fullchain.pem for Nginx/Apache).
2. Verify Hostname Matching
import ssl, socket
hostname = "example.com"
ctx = ssl.create_default_context()
with ctx.wrap_socket(socket.socket(), server_hostname=hostname) as s:
s.connect((hostname, 443))
cert = s.getpeercert()
print(cert["subjectAltName"])
If the output does not contain your hostname, re‑issue the certificate with the correct Subject Alternative Name.
3. Update the Trusted Root Store
Linux (Debian/Ubuntu)
sudo apt-get update && sudo apt-get install --reinstall ca-certificates
sudo update-ca-certificates
Windows PowerShell
Get-ChildItem Cert:\LocalMachine\Root | Where-Object {$_.Subject -match "YourCA"} | Remove-Item
Import-Certificate -FilePath "C:\path\to\YourCA.crt" -CertStoreLocation Cert:\LocalMachine\Root
4. Force a Compatible TLS Version / Cipher Suite
Nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
Apache
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5
Restart the web server after changes.
5. Use Diagnostic Tools
-
Wireshark – Capture the TLS handshake and look for
Alertmessages. -
curl – Add
-vor--tlsv1.2to see detailed handshake logs. - testssl.sh – Comprehensive SSL/TLS testing script.
Example Fix Script
The following Bash script automates the most common fixes (chain concatenation, store update, and service reload). Feel free to adapt it to your environment.
#!/usr/bin/env bash
set -euo pipefail
HOST=$1
CERT_DIR="/etc/ssl/certs"
CHAIN_FILE="${CERT_DIR}/${HOST}.fullchain.pem"
LEAF_CERT="${CERT_DIR}/${HOST}.crt"
INTERMEDIATE="${CERT_DIR}/${HOST}.intermediate.pem"
# 1. Combine leaf + intermediate certificates
cat "$LEAF_CERT" "$INTERMEDIATE" > "$CHAIN_FILE"
echo "[+] Combined certificate chain saved to $CHAIN_FILE"
# 2. Update trust store (Debian/Ubuntu example)
sudo apt-get install -y ca-certificates
sudo update-ca-certificates
echo "[+] Trust store refreshed"
# 3. Reload web server (detect Nginx or Apache)
if systemctl -q is-active nginx; then
sudo systemctl reload nginx
echo "[+] Nginx reloaded"
elif systemctl -q is-active apache2; then
sudo systemctl reload apache2
echo "[+] Apache reloaded"
else
echo "[!] No supported web server detected"
fi
You can download the pre‑configured script here: Download the pre‑configured script here.
Quick Recap
-
Check the chain with
openssl s_client. - Validate hostname using a tiny Python snippet.
- Refresh trust stores on both client and server.
- Align TLS versions and cipher suites in your web server config.
-
Leverage tools like
curl -v, Wireshark, ortestssl.shfor deeper inspection.
If you prefer an all‑in‑one solution, Get the complete patch tool from our repository: Get the complete patch tool.
For a broader context and additional hardening tips, Access the full repository fix here: Access the full repository fix.
Top comments (0)