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:

bash
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. 1.Network latency - High latency between client and server
  2. 2.Large certificate chain - Many certificates to transmit
  3. 3.Slow certificate validation - OCSP/CRL checks timing out
  4. 4.MTU issues - Packet fragmentation in handshake
  5. 5.Cipher negotiation slow - Complex cipher suite selection
  6. 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

CheckCommandExpected
TCP connectionnc -zv host 443Connected
SSL handshakeopenssl s_clientCompletes
Cert chain-showcerts< 5 certs
OCSP/CRL-statusResponds
Timeout configssl_handshake_timeout> 60s
Server loadtopCPU < 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 ```

  • [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)