Сообщение об ошибке
javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
Также выводится как
- Remote host terminated the handshake
JDK 21 и 25 выводят «Remote host terminated the handshake». В журналах Java 8 та же ситуация обычно выглядит как «Remote host closed connection during handshake» с причиной «SSL peer shut down incorrectly».
Java (JSSE) — the TCP connection closed during the handshake without a TLS alert
Что это означает
Другая сторона (или что-то между сторонами) закрыла TCP-соединение, пока JVM выполняла TLS-рукопожатие, и не отправила оповещение TLS. Java не может знать причину: она видит только, что соединение оборвалось. Обычно межсетевой экран, прокси или балансировщик нагрузки обрывает рукопожатие, либо сервер молча отклоняет ClientHello.
Почему это происходит
Прокси или межсетевой экран на пути трафика
частоСети, где обязателен HTTPS-прокси, межсетевые экраны с глубокой инспекцией пакетов, блокирующие по SNI или отпечатку JA3/JA4, и VPN часто молча обрывают заблокированные соединения.
Сервер обрывает рукопожатия, которые отклоняет
частоНекоторые серверы и балансировщики нагрузки, если их не устраивает версия протокола, список шифров или SNI, закрывают соединение вместо того, чтобы ответить оповещением.
SNI не передан
иногдаПри подключении по IP-адресу или с jsse.enableSNIExtension=false имя сервера не передаётся; пограничные узлы CDN обычно обрывают такие рукопожатия.
Лимиты соединений и ограничение частоты запросов
иногдаСерверы, исчерпавшие лимит соединений, или ограничители частоты запросов при всплесках трафика сбрасывают новые соединения; тогда ошибка возникает время от времени.
Как исправить
- 1.
Отследите рукопожатие и сравните с openssl
javax.net.debug показывает, до какого этапа дошло рукопожатие; если соединение openssl s_client с того же хоста тоже обрывается, причина в сети или сервере, а не в 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.
Используйте прокси вашей сети
По умолчанию Java не использует настройки прокси ОС или браузера; задайте https.proxyHost/https.proxyPort (или -Djava.net.useSystemProxies=true) и исключите внутренние хосты через 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.
Передавайте SNI
Указывайте в URL имя хоста и не отключайте расширение 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.