What's Actually Happening
SSL/TLS handshake fails due to timeout before completing the connection negotiation. Client receives connection timeout or reset during secure connection establishment.
The Error You'll See
Client timeout:
```bash $ curl -v https://example.com
* Trying 192.168.1.10:443... * Connected to example.com (192.168.1.10) port 443 * OpenSSL SSL_connect: Connection timed out in connection to example.com:443 curl: (28) Operation timed out after 30001 milliseconds with 0 bytes received ```
Browser error:
ERR_CONNECTION_TIMED_OUT
This site can't be reached. example.com took too long to respond.Server logs:
```bash $ tail /var/log/nginx/error.log
2026-04-16T00:55:00Z [error] SSL handshake timed out (110: Connection timed out) while SSL handshaking ```
Why This Happens
- 1.Network latency - High latency between client and server
- 2.Large certificate chain - Many certificates to transmit
- 3.Slow certificate validation - OCSP/CRL checks timing out
- 4.MTU issues - Packet fragmentation in handshake
- 5.Cipher negotiation slow - Complex cipher suite selection
- 6.Server overload - High load causing slow response
Step 1: Check Basic Connectivity
```bash # Test basic TCP connectivity nc -zv example.com 443
# Test with timeout nc -zv -w 5 example.com 443
# Check if port is open nmap -p 443 example.com
# Test HTTP (non-SSL) if available curl -v http://example.com --connect-timeout 10
# Check network latency ping -c 5 example.com traceroute example.com mtr example.com
# Check if connection established ss -tnp | grep :443 ```
Step 2: Test SSL Handshake with OpenSSL
```bash # Test SSL connection openssl s_client -connect example.com:443 -servername example.com
# With timeout timeout 10 openssl s_client -connect example.com:443 -servername example.com
# Show certificate chain openssl s_client -connect example.com:443 -servername example.com -showcerts
# Test specific protocol openssl s_client -connect example.com:443 -tls1_2
# Test with debug openssl s_client -connect example.com:443 -servername example.com -debug -msg
# Check certificate verification openssl s_client -connect example.com:443 -servername example.com -verify_return_error
# Check OCSP response openssl s_client -connect example.com:443 -servername example.com -status ```
Step 3: Check Certificate Chain Size
```bash # Check certificate chain openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | openssl x509 -text -noout | grep -E "Subject:|Issuer:"
# Get certificate chain size echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -c "BEGIN CERTIFICATE"
# Large certificate chains = more data to transmit # If chain has 10+ certificates, handshake may timeout on slow connections
# Check certificate sizes echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' | wc -c
# Optimize: Remove unnecessary intermediate certificates # Keep only: Server cert -> Intermediate CA -> Root CA ```
Step 4: Check Certificate Validation
```bash # Check OCSP responder openssl x509 -in server.crt -text -noout | grep "OCSP - URI"
# Test OCSP responder openssl ocsp -issuer intermediate.crt -cert server.crt -url http://ocsp.example.com -resp_text
# Check CRL openssl x509 -in server.crt -text -noout | grep "CRL Distribution"
# Download CRL curl -o crl.pem http://crl.example.com/crl.pem
# If OCSP/CRL servers slow, validation times out # Check OCSP responder connectivity: curl -v http://ocsp.example.com
# Disable OCSP stapling if causing issues: # In Nginx: ssl_stapling off;
# In Apache: SSLUseStapling off ```
Step 5: Check Server SSL Configuration
```bash # Check Nginx SSL configuration grep -E "ssl_|timeout" /etc/nginx/nginx.conf
# Key settings for handshake timeout: ssl_handshake_timeout 60s; # Nginx default
# Increase if needed: ssl_handshake_timeout 120s;
# Check protocols and ciphers openssl ciphers -v 'HIGH:!aNULL:!MD5'
# Check server cipher order grep ssl_prefer_server_ciphers /etc/nginx/nginx.conf
# For Apache: grep -E "SSL|Timeout" /etc/apache2/sites-enabled/default-ssl.conf
# Key settings: Timeout 300 SSLProtocol all -SSLv2 -SSLv3
# Check for slow cipher suites nmap --script ssl-enum-ciphers -p 443 example.com ```
Step 6: Optimize Cipher Suite Configuration
```nginx # Nginx - Use efficient cipher suites
ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
# TLS 1.3 is faster (fewer round trips) ssl_protocols TLSv1.3 TLSv1.2;
# Enable session caching for faster resumption ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off;
# OCSP stapling (reduces client validation time) ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 valid=300s;
# Apache configuration: SSLProtocol all -SSLv2 -SSLv3 SSLHonorCipherOrder on SSLCipherSuite HIGH:!aNULL:!MD5
SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) SSLSessionCacheTimeout 300 ```
Step 7: Check MTU and Packet Size
```bash # Check MTU ip link show eth0
# Check for fragmentation ping -M do -s 1472 example.com # Test with max unfragmented size ping -M do -s 1473 example.com # Should fail if fragmentation blocked
# If MTU issues, handshake packets may be dropped # Lower MTU or enable PMTUD
# Check for dropped packets netstat -s | grep -i fragment
# TCP MSS clamping: # On server firewall: iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
# Or set specific MSS: iptables -t mangle -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1460 ```
Step 8: Increase Handshake Timeout
```nginx # Nginx - Increase SSL handshake timeout
http { ssl_handshake_timeout 120s; # Increase from default 60s }
# Also increase client body timeout client_body_timeout 120s; client_header_timeout 120s; send_timeout 120s;
# Apache - Increase timeout Timeout 600 SSLSessionCacheTimeout 600
# HAProxy: timeout connect 30s timeout client 60s timeout server 60s
# Application-level (Node.js): const tls = require('tls'); const socket = tls.connect({ host: 'example.com', port: 443, timeout: 120000 // 2 minutes });
# Java: System.setProperty("sun.security.ssl.handshakeTimeout", "120000"); ```
Step 9: Check Server Load
```bash # Check server CPU usage top -n 1 | head -20
# Check SSL-specific load # Nginx status: curl http://localhost/nginx_status
# Check SSL connections ss -tnp state established '( dport = :443 or sport = :443 )' | wc -l
# Check for blocking operations strace -p $(pgrep nginx | head -1) -e trace=read,write -f
# Check TLS acceleration # If CPU high, consider: # 1. Hardware TLS accelerator # 2. TLS offload to load balancer # 3. Session caching/resumption
# Check entropy for random numbers cat /proc/sys/kernel/random/entropy_avail # Should be > 1000 # If low, install haveged: apt-get install haveged ```
Step 10: Monitor SSL Handshake Performance
```bash # Create monitoring script cat << 'EOF' > monitor_ssl_handshake.sh #!/bin/bash HOST=example.com PORT=443
echo "=== SSL Handshake Time ===" time openssl s_client -connect $HOST:$PORT -servername $HOST </dev/null 2>&1 | grep -E "Verify|depth"
echo "" echo "=== Certificate Chain ===" echo | openssl s_client -connect $HOST:$PORT -servername $HOST -showcerts 2>/dev/null | grep -E "s:|i:" | head -10
echo "" echo "=== Protocol and Cipher ===" echo | openssl s_client -connect $HOST:$PORT -servername $HOST 2>/dev/null | grep -E "Protocol|Cipher"
echo "" echo "=== OCSP Status ===" echo | openssl s_client -connect $HOST:$PORT -servername $HOST -status 2>/dev/null | grep -A 10 "OCSP response"
echo "" echo "=== Connection Test ===" curl -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nSSL: %{time_appconnect}s\nTotal: %{time_total}s\n" -o /dev/null -s https://$HOST/ EOF
chmod +x monitor_ssl_handshake.sh
# Monitor with Prometheus: # ssl_handshake_duration_seconds # ssl_handshake_errors_total # ssl_session_cache_hits ```
SSL Handshake Timeout Checklist
| Check | Command | Expected |
|---|---|---|
| TCP connection | nc -zv host 443 | Connected |
| SSL handshake | openssl s_client | Completes |
| Cert chain | -showcerts | < 5 certs |
| OCSP/CRL | -status | Responds |
| Timeout config | ssl_handshake_timeout | > 60s |
| Server load | top | CPU < 80% |
Verify the Fix
```bash # After increasing timeout or fixing configuration
# 1. Test SSL handshake completes openssl s_client -connect example.com:443 -servername example.com </dev/null // Should show certificate and "Verify return code: 0"
# 2. Test with curl curl -v https://example.com // Should complete without timeout
# 3. Measure handshake time time openssl s_client -connect example.com:443 </dev/null // Should complete in < 5 seconds normally
# 4. Test multiple connections for i in {1..10}; do curl -s -o /dev/null -w "%{time_total}\n" https://example.com; done // Consistent response times
# 5. Check server logs tail /var/log/nginx/error.log | grep -i "handshake|timeout" // No timeout errors ```
Related Issues
- [Fix SSL Certificate Expired](/articles/fix-ssl-certificate-expired)
- [Fix SSL Certificate Chain Incomplete](/articles/fix-ssl-certificate-chain-incomplete)
- [Fix SSL Certificate Not Trusted](/articles/fix-ssl-certificate-not-trusted)