Mensaje de error
javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
También se muestra como
- Remote host terminated the handshake
JDK 21 y 25 imprimen «Remote host terminated the handshake». En los registros de Java 8, la misma situación suele aparecer como «Remote host closed connection during handshake», con la causa «SSL peer shut down incorrectly».
Java (JSSE) — the TCP connection closed during the handshake without a TLS alert
Qué significa
El otro extremo (o algo intermedio) cerró la conexión TCP mientras la JVM estaba en pleno handshake TLS, sin enviar ninguna alerta TLS. Java no puede saber el motivo: solo ve que la conexión termina. Normalmente, un firewall, un proxy o un balanceador de carga corta el handshake, o el servidor rechaza el ClientHello en silencio.
Por qué ocurre
Un proxy o firewall en el camino
frecuenteLas redes que exigen un proxy HTTPS, los firewalls de inspección profunda de paquetes que bloquean por SNI o por huella JA3/JA4 y las VPN suelen terminar en silencio las conexiones bloqueadas.
El servidor cierra los handshakes que rechaza
frecuenteAlgunos servidores y balanceadores de carga cierran la conexión en lugar de responder con una alerta cuando la versión del protocolo, la lista de cifrados o el SNI no son aceptables.
SNI ausente
ocasionalConectarse por dirección IP, o con jsse.enableSNIExtension=false, no envía ningún nombre de servidor; los nodos perimetrales de las CDN suelen descartar esos handshakes.
Límites de conexiones y de frecuencia
ocasionalLos servidores que alcanzan su límite de conexiones, o los limitadores de frecuencia ante picos de tráfico, restablecen las conexiones nuevas; entonces el error aparece de forma intermitente.
Cómo solucionarlo
- 1.
Trazar el handshake y comparar con openssl
javax.net.debug muestra hasta dónde llegó el handshake; si openssl s_client desde el mismo host también se corta, la causa está en la red o en el servidor, no en la JVM.
bash java -Djavax.net.debug=ssl:handshake -jar app.jar 2>&1 | grep -E 'ClientHello|server_name|EOF|terminated' # Same handshake without Java: is it the network or the JVM? openssl s_client -connect api.example.com:443 -servername api.example.com -brief </dev/null - 2.
Usar el proxy de la red
Por defecto, Java no usa la configuración de proxy del sistema operativo ni la del navegador; define https.proxyHost/https.proxyPort (o -Djava.net.useSystemProxies=true) y excluye los hosts internos con http.nonProxyHosts.
bash # Corporate networks: route HTTPS through the proxy the browser uses java -Dhttps.proxyHost=proxy.example.net -Dhttps.proxyPort=3128 \ -Dhttp.nonProxyHosts="localhost|*.internal.example.com" -jar app.jar - 3.
Enviar el SNI
Usa el nombre de host en la URL y mantén activada la extensión SNI.
text Connect with the host name (not an IP address) so JSSE sends SNI, and do not set -Djsse.enableSNIExtension=false: front ends that need SNI close such connections.