Mensagem de erro
javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
Também aparece como
- Remote host terminated the handshake
Os JDKs 21 e 25 imprimem “Remote host terminated the handshake”. Logs do Java 8 costumam mostrar a mesma situação como o erro “Remote host closed connection during handshake”, causado por “SSL peer shut down incorrectly”.
Java (JSSE) — the TCP connection closed during the handshake without a TLS alert
O que significa
O outro lado (ou algo no meio do caminho) fechou a conexão TCP enquanto a JVM estava no handshake TLS, sem enviar um alerta TLS. O Java não tem como saber o motivo; ele só vê a conexão terminar. Normalmente, um firewall, um proxy ou um balanceador de carga derruba o handshake, ou o servidor rejeita o ClientHello em silêncio.
Por que acontece
Um proxy ou firewall no caminho
comumRedes que exigem um proxy HTTPS, firewalls com inspeção profunda de pacotes que bloqueiam por SNI ou pela impressão digital JA3/JA4 e VPNs costumam encerrar em silêncio as conexões bloqueadas.
O servidor fecha os handshakes que rejeita
comumAlguns servidores e balanceadores de carga fecham a conexão, em vez de responder com um alerta, quando a versão do protocolo, a lista de cifras ou o SNI não são aceitáveis.
SNI ausente
ocasionalConectar por endereço IP, ou com jsse.enableSNIExtension=false, não envia nenhum nome de servidor; os servidores de borda de CDNs costumam derrubar esses handshakes.
Limites de conexão e limitação de taxa
ocasionalServidores no limite de conexões, ou limitadores de taxa sob picos de tráfego, encerram abruptamente as novas conexões; o erro então aparece de forma intermitente.
Como corrigir
- 1.
Rastrear o handshake e comparar com o openssl
O javax.net.debug mostra até onde o handshake chegou; se o openssl s_client no mesmo host também for interrompido, a causa é a rede ou o servidor, não a 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 o proxy da rede
Por padrão, o Java não usa as configurações de proxy do sistema operacional nem do navegador; defina https.proxyHost/https.proxyPort (ou -Djava.net.useSystemProxies=true) e exclua os hosts internos com 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 o SNI
Use o nome do host na URL e mantenha a extensão SNI ativada.
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.