Introduction

Apache mod_ssl connection reset errors occur when SSL/TLS connections are abruptly terminated during handshake or data transfer, causing clients to receive "connection reset by peer" errors. This manifests as ERR_SSL_PROTOCOL_ERROR in browsers, curl failures with SSL_connect errors, and incomplete HTTPS connections. Common causes include SSL certificate chain validation failures (missing intermediate certificates), TLS protocol version mismatch (client/server support different versions), cipher suite incompatibility (no common cipher negotiated), OCSP stapling timeout or failure, SSL session cache exhaustion, client certificate verification failures, SNI (Server Name Indication) configuration errors, SSL buffer size issues for large certificates, firewall or security software intercepting SSL connections, and expired or revoked certificates. The fix requires diagnosing whether the issue is certificate-related, protocol-related, or infrastructure-related. This guide provides production-proven troubleshooting for Apache mod_ssl connection reset errors across various deployment scenarios.

Symptoms

  • Browser shows ERR_SSL_PROTOCOL_ERROR or ERR_CONNECTION_RESET
  • curl fails with "SSL_connect: connection reset" error
  • Apache error log shows "SSL library error" or "handshake failure"
  • HTTPS connections intermittently reset during handshake
  • Some clients connect successfully while others fail
  • Connection reset occurs at consistent SSL record boundaries
  • OCSP stapling causes connection timeouts
  • Client certificate authentication fails unexpectedly
  • TLS 1.3 clients cannot connect after server upgrade
  • HSTS header sent but connection reset before redirect

Common Causes

  • SSL certificate chain incomplete (missing intermediate CA)
  • TLS protocol version not supported by client or server
  • Cipher suite configuration too restrictive or incompatible
  • OCSP stapling timeout (OCSP responder unreachable)
  • SSL session cache full or misconfigured
  • Client certificate required but not provided or invalid
  • SNI not supported by client (older clients, some libraries)
  • SSL buffer size too small for certificate chain
  • Firewall/IDS intercepting and resetting SSL connections
  • Certificate expired, revoked, or hostname mismatch
  • SSL compression disabled but client requires it
  • HTTP/2 configuration conflicting with SSL settings

Step-by-Step Fix

### 1. Diagnose SSL connection reset

Check Apache SSL configuration:

```bash # View SSL module configuration # Common locations: # /etc/httpd/conf.d/ssl.conf # /etc/apache2/mods-available/ssl.conf # /etc/apache2/sites-available/default-ssl.conf

grep -r "SSLEngine\|SSLCertificate\|SSLProtocol\|SSLCipher" /etc/httpd/ # Or for Debian/Ubuntu grep -r "SSLEngine\|SSLCertificate\|SSLProtocol\|SSLCipher" /etc/apache2/

# Check error log for SSL errors tail -100 /var/log/httpd/error_log | grep -i "ssl\|tls" # Or for Debian/Ubuntu tail -100 /var/log/apache2/error.log | grep -i "ssl\|tls"

# Common error messages: # AH02032: Hostname provided via SNI not present in ServerName # AH02008: SSL library error: handshake failed # AH01904: unable to start SSL engine # AH02217: ssl_stapling_init_cert: can't retrieve issuer certificate

# Test SSL configuration apachectl configtest # Or apache2ctl configtest

# Check loaded SSL module apache2ctl -M | grep ssl # Should show: ssl_module (shared) ```

Test SSL connection with openssl:

```bash # Basic SSL connection test openssl s_client -connect example.com:443 -servername example.com

# Check certificate chain openssl s_client -connect example.com:443 -showcerts

# Test specific TLS version openssl s_client -connect example.com:443 -tls1_2 openssl s_client -connect example.com:443 -tls1_3

# Test cipher suite openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384'

# Check certificate verification openssl s_client -connect example.com:443 -verify_return_error

# Look for: # - Verify return code: 0 (ok) = Success # - Verify return code: 20 = Unable to get local issuer certificate # - Verify return code: 21 = Unable to verify the first certificate ```

Analyze SSL handshake with tcpdump:

```bash # Capture SSL handshake tcpdump -i any -s 0 -w ssl_handshake.pcap host example.com and port 443

# Analyze with Wireshark wireshark ssl_handshake.pcap

# Look for: # - Client Hello: TLS version, cipher suites, SNI # - Server Hello: Selected TLS version, cipher suite # - Certificate: Server certificate chain # - Alert: Error codes (handshake_failure, protocol_version, etc.) # - RST flag: Connection reset indicator

# Quick analysis with tshark tshark -r ssl_handshake.pcap -Y "ssl.handshake.type == 1" -T fields -e ssl.handshake.extensions_server_name tshark -r ssl_handshake.pcap -Y "ssl.alert_message" -T fields -e ssl.alert_description ```

### 2. Fix certificate chain issues

Install complete certificate chain:

```bash # Certificate files needed: # - Server certificate (your domain) # - Intermediate certificate(s) (CA chain) # - Root certificate (usually not needed, in client trust store)

# Combine server certificate with intermediate(s) # Order matters: server cert first, then intermediates in order

cat > /etc/ssl/certs/example.com.bundle.crt << 'EOF' -----BEGIN CERTIFICATE----- [Your server certificate] -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- [Intermediate CA certificate 1] -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- [Intermediate CA certificate 2] -----END CERTIFICATE----- EOF

# Or concatenate existing files cat server.crt intermediate-ca.crt > /etc/ssl/certs/example.com.bundle.crt

# Configure Apache to use bundle # /etc/httpd/conf.d/ssl.conf or virtual host SSLCertificateFile /etc/ssl/certs/example.com.bundle.crt SSLCertificateKeyFile /etc/ssl/private/example.com.key

# Alternative: Separate chain file (Apache 2.4+) SSLCertificateFile /etc/ssl/certs/server.crt SSLCertificateKeyFile /etc/ssl/private/server.key SSLCertificateChainFile /etc/ssl/certs/intermediate-ca.crt

# Verify certificate chain openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt /etc/ssl/certs/example.com.bundle.crt

# Check certificate details openssl x509 -in /etc/ssl/certs/server.crt -text -noout

# Check issuer chain openssl x509 -in /etc/ssl/certs/server.crt -noout -issuer openssl x509 -in /etc/ssl/certs/intermediate-ca.crt -noout -subject ```

Verify certificate chain remotely:

```bash # Use online tools # https://www.ssllabs.com/ssltest/ # https://crt.sh/

# Or with openssl from command line openssl s_client -connect example.com:443 -showcerts 2>/dev/null | \ openssl x509 -noout -issuer -subject

# Check for missing intermediate # If issuer doesn't match any subject in chain, intermediate is missing

# Test with curl curl -vI https://example.com 2>&1 | grep -E "SSL|subject|issuer" ```

### 3. Configure TLS protocol versions

Set appropriate TLS versions:

```apache # /etc/httpd/conf.d/ssl.conf or virtual host

# Modern configuration (TLS 1.2 and 1.3 only) SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

# Or explicitly enable only specific versions SSLProtocol -all +TLSv1.2 +TLSv1.3

# For compatibility with older clients (not recommended for new deployments) SSLProtocol all -SSLv2 -SSLv3

# TLS 1.3 specific settings (Apache 2.4.46+) SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2 +TLSv1.3

# TLS 1.3 cipher suites (separate from TLS 1.2) SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 ```

Test TLS version compatibility:

```bash # Test TLS 1.2 openssl s_client -connect example.com:443 -tls1_2 </dev/null 2>&1 | grep -E "Protocol|Cipher"

# Test TLS 1.3 openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>&1 | grep -E "Protocol|Cipher"

# Test TLS 1.1 (should fail with modern config) openssl s_client -connect example.com:443 -tls1_1 </dev/null 2>&1 | grep -E "Protocol|error"

# Test TLS 1.0 (should fail with modern config) openssl s_client -connect example.com:443 -tls1 </dev/null 2>&1 | grep -E "Protocol|error"

# Test SSLv3 (should always fail) openssl s_client -connect example.com:443 -ssl3 </dev/null 2>&1 | grep -E "Protocol|error"

# Scan all supported protocols nmap --script ssl-enum-ciphers -p 443 example.com ```

### 4. Configure cipher suites

Set secure cipher configuration:

```apache # Modern cipher suite configuration # Prioritize ECDHE for forward secrecy

SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384

# SSLHonorCipherOrder: Prefer server cipher order SSLHonorCipherOrder on

# For compatibility with older clients (legacy, use with caution) #SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4:!3DES

# Compression (disable to prevent CRIME attack) SSLCompression off

# Session configuration SSLSessionCache "shmcb:logs/ssl_scache(512000)" SSLSessionCacheTimeout 300 ```

Test cipher suite compatibility:

```bash # Test specific cipher openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384' </dev/null 2>&1 | grep Cipher

# List all supported ciphers nmap --script ssl-enum-ciphers -p 443 example.com

# Test with curl curl -vI --ciphers ECDHE-RSA-AES256-GCM-SHA384 https://example.com

# Check cipher strength openssl ciphers -v 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' | column -t ```

### 5. Fix OCSP stapling issues

Configure OCSP stapling:

```apache # Enable OCSP stapling SSLUseStapling on

# OCSP responder timeout (default: 10 seconds) SSLStaplingResponderTimeout 5

# OCSP response cache time SSLStaplingResponseMaxAge 86400

# OCSP responder connection timeout SSLStaplingConnectTimeout 5000

# OCSP responder IP resolution SSLStaplingForceURL "http://ocsp.example.com"

# Fake OCSP for testing (do not use in production) # SSLStaplingFake off

# OCSP responder verification SSLStaplingVerify on SSLCACertificateFile /etc/ssl/certs/ca-bundle.crt ```

Check OCSP stapling status:

```bash # Check if OCSP stapling is enabled openssl s_client -connect example.com:443 -status -servername example.com </dev/null 2>&1 | grep -A 10 "OCSP response"

# Look for: # - OCSP response status: successful # - Certificate status: good # - This Update: [recent date]

# Check Apache OCSP status # /var/log/httpd/error_log or /var/log/apache2/error.log grep -i "stapling" /var/log/httpd/error_log | tail -20

# Common errors: # - ssl_stapling_init_cert: can't retrieve issuer certificate # - ssl_stapling_check_cert: can't get OCSP response # - ssl_stapling: no OCSP responder URL found

# Test OCSP responder directly openssl ocsp -issuer intermediate-ca.crt -cert server.crt \ -url http://ocsp.example.com -header "Host" "ocsp.example.com" ```

Disable OCSP stapling if causing issues:

```apache # Temporarily disable OCSP stapling for troubleshooting SSLUseStapling off

# Or disable verification (not recommended for production) SSLStaplingVerify off ```

### 6. Configure SSL session cache

Configure SSL session cache:

```apache # Shared memory cache (recommended) SSLSessionCache "shmcb:/var/run/apache2/ssl_scache(512000)" SSLSessionCacheTimeout 300

# For systemd-based systems, ensure directory exists # Create /etc/tmpfiles.d/apache2.conf d /var/run/apache2 0755 root root -

# Alternative: DBM cache # SSLSessionCache "dbm:/var/run/apache2/ssl_scache"

# Alternative: External memcached (for load balancers) # SSLSessionCache "external:127.0.0.1:11211"

# Monitor cache usage # Enable status module a2enmod status

# Check cache statistics curl http://localhost/server-status?auto | grep -i ssl ```

Fix SSL session cache permissions:

```bash # Create SSL cache directory mkdir -p /var/run/apache2 chown www-data:www-data /var/run/apache2 chmod 755 /var/run/apache2

# Or for RHEL/CentOS mkdir -p /var/run/httpd chown apache:apache /var/run/httpd chmod 755 /var/run/httpd

# Restart Apache systemctl restart apache2 # Or systemctl restart httpd

# Verify cache is working # Check for these in error log: # - AH00063: unable to create shmcb session database # - AH01662: SSLSessionCache: cache directory exists but is not writable

# Test SSL session resumption openssl s_client -connect example.com:443 -reuse </dev/null 2>&1 | grep -i "session" ```

### 7. Fix client certificate authentication

Configure client certificate verification:

```apache # Require client certificate SSLVerifyClient require SSLVerifyDepth 2

# Or require only for specific paths <Location /secure> SSLVerifyClient require SSLVerifyDepth 2 </Location>

# Optional client certificate (check in application) SSLVerifyClient optional SSLVerifyDepth 2

# CA certificate for client verification SSLCACertificateFile /etc/ssl/certs/client-ca.crt

# Or CA bundle for multiple CAs SSLCACertificatePath /etc/ssl/certs/client-cas/

# CRL (Certificate Revocation List) checking SSLCARevocationFile /etc/ssl/certs/client-ca.crl SSLCARevocationCheck leaf

# OCSP checking for client certificates SSLOCSPDefaultResponder "http://ocsp.example.com" SSLOCSPOverrideResponder on ```

Generate client certificate:

```bash # Generate client private key openssl genrsa -out client.key 2048

# Generate CSR openssl req -new -key client.key -out client.csr \ -subj "/CN=client.example.com/O=Example Inc"

# Sign with CA openssl x509 -req -in client.csr -CA client-ca.crt -CAkey client-ca.key \ -CAcreateserial -out client.crt -days 365 -sha256

# Convert to PKCS12 for browser import openssl pkcs12 -export -out client.p12 -inkey client.key -in client.crt \ -certfile client-ca.crt

# Test client certificate curl -v --cert client.crt --key client.key https://example.com/secure ```

### 8. Fix SNI configuration

Configure Server Name Indication:

```apache # Modern Apache with SNI support (2.2.12+) # Each virtual host needs its own SSL configuration

<VirtualHost *:443> ServerName example.com ServerAlias www.example.com

SSLEngine on SSLCertificateFile /etc/ssl/certs/example.com.crt SSLCertificateKeyFile /etc/ssl/private/example.com.key SSLCertificateChainFile /etc/ssl/certs/intermediate.crt

# DocumentRoot and other settings DocumentRoot /var/www/example.com </VirtualHost>

<VirtualHost *:443> ServerName other.example.com

SSLEngine on SSLCertificateFile /etc/ssl/certs/other.crt SSLCertificateKeyFile /etc/ssl/private/other.key

DocumentRoot /var/www/other </VirtualHost>

# For clients without SNI support (IE on Windows XP, very old Android) # Use a default certificate SSLStrictSNIVHostCheck off ```

Test SNI configuration:

```bash # Test SNI with openssl openssl s_client -connect example.com:443 -servername example.com </dev/null 2>&1 | grep "subject="

# Test different virtual hosts openssl s_client -connect example.com:443 -servername other.example.com </dev/null 2>&1 | grep "subject="

# Test without SNI (should get default certificate) openssl s_client -connect example.com:443 </dev/null 2>&1 | grep "subject="

# Check Apache SNI support apache2ctl -V | grep -i ssl # Should show: -D SSL_LIBRARY_VERSION=OpenSSL

# For old clients, check User-Agent and redirect # Or use separate IP addresses ```

Prevention

  • Always use complete certificate chain (server + intermediates)
  • Configure TLS 1.2 and 1.3 only for modern deployments
  • Use strong cipher suites with forward secrecy (ECDHE)
  • Enable OCSP stapling with appropriate timeouts
  • Configure SSL session cache for session resumption
  • Monitor certificate expiration and set up renewal alerts
  • Test SSL configuration with tools like SSL Labs
  • Document SSL configuration for each virtual host
  • Use HSTS to enforce HTTPS after initial testing
  • Implement certificate transparency monitoring
  • **502 Bad Gateway**: Upstream SSL certificate validation failed
  • **503 Service Unavailable**: SSL handshake failed with backend
  • **504 Gateway Timeout**: SSL connection to backend timed out
  • **400 Bad Request**: SSL required but HTTP used
  • **SSL_ERROR_RX_RECORD_TOO_LONG**: HTTP on HTTPS port