Коротко: ERR_SSL_PROTOCOL_ERROR значит, что Chrome начал TLS-рукопожатие, получил в ответ что-то, что не является корректным TLS, и прервал соединение. Чаще всего порт за адресом https:// отвечает по обычному HTTP: nginx с listen 443; без ssl, порт Docker, проброшенный не на тот порт контейнера, или dev-сервер на localhost. Если ошибку видите только вы, а у других сайт открывается, HTTPS мешает что-то на вашем устройстве или в вашей сети.
В русском Chrome над кодом написано Этот сайт не может обеспечить безопасное подключение и Сайт example.com отправил недействительный ответ., в английском — This site can’t provide a secure connection. Edge и Яндекс Браузер построены на Chromium и показывают тот же код ERR_SSL_PROTOCOL_ERROR, так что причины и проверки у них те же.
Две команды с любой машины, где есть curl, покажут, на чьей стороне проблема:
# 1. Где обрывается рукопожатие?
curl -sv -o /dev/null https://example.com/
# 2. Отвечает ли порт 443 обычным HTTP?
curl -sS -i http://example.com:443/Замените example.com на проблемный хост. Порт, который действительно ждёт TLS, вторую команду отвергнет: nginx и Cloudflare отвечают 400 The plain HTTP request was sent to HTTPS port, Apache — You’re speaking plain HTTP to an SSL-enabled server port, Caddy и другие серверы на Go — Client sent an HTTP request to an HTTPS server. Если же пришла обычная страница или редирект, порт 443 отвечает по обычному HTTP — ошибка найдена: причина 1. Сообщения ниже получены в curl 7.88 с OpenSSL 3.0 на Debian 12; LibreSSL в macOS формулирует их немного иначе. curl, OpenSSL и nginx свои сообщения не переводят, поэтому на сервере с русской локалью текст будет тем же.
Какое сообщение вы видите?
| Клиент | Сообщение | Обычно означает |
|---|---|---|
| Chrome, Edge | ERR_SSL_PROTOCOL_ERROR; в русском Chrome под заголовком Этот сайт не может обеспечить безопасное подключение и Сайт example.com отправил недействительный ответ., в английском — This site can’t provide a secure connection и example.com sent an invalid response. | Ответ был не TLS, или рукопожатие сорвалось нестандартно: обычный HTTP на 443, другой сервис, фильтр |
| Яндекс Браузер | ERR_SSL_PROTOCOL_ERROR; в справке Браузера рядом с ней разобраны сообщения «Этот сайт не может обеспечить безопасное соединение» и «Сайт отправил некорректный ответ» | То же, что в Chrome: Браузер построен на Chromium |
| Chrome, Edge | localhost sent an invalid response.; в русском Chrome — Сайт localhost отправил недействительный ответ. | Dev-сервер работает по HTTP, а вы запросили HTTPS: localhost |
| Firefox | Secure Connection Failed (в русском Firefox — Ошибка при установлении защищённого соединения) · SSL_ERROR_RX_RECORD_TOO_LONG | Обычный HTTP на HTTPS-порту |
| Firefox | PR_END_OF_FILE_ERROR | Соединение оборвалось посреди рукопожатия: прокси, VPN, антивирус, фильтр |
| Safari | Safari can’t establish a secure connection to the server | Любой сбой TLS; запустите проверку curl выше |
| curl | curl: (35) … wrong version number | Обычный HTTP или другой протокол на порту |
| Chrome, Edge | uses an unsupported protocol. (в русском Chrome — На сайте example.com используется неподдерживаемый протокол.) · ERR_SSL_VERSION_OR_CIPHER_MISMATCH | Не эта ошибка: нет общей версии TLS или шифра |
| Chrome, Edge | Your connection is not private (в русском Chrome — Подключение не защищено) · NET::ERR_CERT_… | Не эта ошибка: рукопожатие прошло, а сертификат отклонён |
| Страница Cloudflare | Error 525 SSL handshake failed | Тот же сбой, но между Cloudflare и вашим сервером: Cloudflare |
Что на самом деле означает ошибка
Когда рукопожатие не удаётся, Chromium переводит причину из BoringSSL в код net::ERR_*. Для нескольких причин есть собственные коды, всё остальное попадает в ERR_SSL_PROTOCOL_ERROR (код -107). Это код «для всего остального», поэтому советы по нему так разбросаны.
Соответствие ниже взято из openssl_ssl_util.cc в исходниках Chromium; строки curl — настоящий вывод, полученный от example.com:80 и от локальных тестовых серверов, которые отправляют именно такой ответ:
| Что прислал сервер | Что пишет curl | Что показывает Chrome |
|---|---|---|
| Обычный HTTP или баннер SSH | wrong version number | ERR_SSL_PROTOCOL_ERROR |
| TLS-алерт access_denied (49), хотя клиентский сертификат не запрашивался | tlsv1 alert access denied | ERR_SSL_PROTOCOL_ERROR; в Chromium отмечено, что так страницу блокируют некоторые файрволы |
| TLS-алерт illegal_parameter (47) | sslv3 alert illegal parameter | ERR_SSL_PROTOCOL_ERROR |
| TLS-алерт handshake_failure (40) сразу после ClientHello | sslv3 alert handshake failure | ERR_SSL_VERSION_OR_CIPHER_MISMATCH |
| TLS-алерт protocol_version (70), например от сервера только с TLS 1.0/1.1 | tlsv1 alert protocol version или unsupported protocol, если сервер отвечает старым ServerHello (с --tlsv1.2) | ERR_SSL_VERSION_OR_CIPHER_MISMATCH |
| TLS-алерт unrecognized_name (112) | tlsv1 unrecognized name | ERR_SSL_UNRECOGNIZED_NAME_ALERT |
| Ничего, соединение закрыто | SSL_ERROR_SYSCALL | ERR_CONNECTION_CLOSED |
| Сброс TCP | Recv failure: Connection reset by peer | ERR_CONNECTION_RESET |
curl 7.88 всё ещё предлагает TLS 1.0 и 1.1; на таком сервере OpenSSL 3.0 затем останавливается с legacy sigalg disallowed or unsupported. Добавьте --tlsv1.2, чтобы проверять так, как подключается браузер. Просроченный сертификат, старая версия TLS или неверные часы в современном Chrome не дают ERR_SSL_PROTOCOL_ERROR: у них свои коды.
Если ошибку видите только вы: проверки на своём устройстве
Откройте адрес на телефоне через мобильный интернет. Если там сайт загружается, причина на вашем компьютере или в вашей сети:
- Окно инкогнито и второй браузер. В режиме инкогнито расширения выключены, если вы сами их там не разрешили. Расширения для VPN и прокси — частые виновники, как и системный прокси в сетевых настройках ОС.
- Проверка HTTPS в антивирусе и корпоративные файрволы. Они встраиваются в каждое рукопожатие, и когда спотыкаются, Chrome получает мусор. Для проверки выключите сканирование HTTPS или фильтрацию SSL/TLS: у Kaspersky это раздел «Проверка защищенных соединений» в настройках сети с вариантом «Не проверять защищенные соединения», у ESET — параметр «Включить SSL/TLS» в разделе «Расширенные параметры → Защита → SSL/TLS». На сайте, который работает, нажмите значок слева от адреса, затем Подключение защищено и Действительный сертификат (в английском Chrome — Connection is secure и Certificate is valid): если издатель назван по имени вашего антивируса или работодателя, ваш HTTPS перехватывают.
- Постквантовый обмен ключами через старые шлюзы инспекции. Начиная с версии 131 Chrome предлагает в рукопожатии TLS 1.3 постквантовый key share на основе ML-KEM. Согласно заметке Fortinet об известной проблеме, глубокая инспекция FortiGate в режиме flow-based со старыми движками IPS отвечает на неё алертом illegal_parameter, а это
ERR_SSL_PROTOCOL_ERROR. Лечится обновлением файрвола, а политика ChromePostQuantumKeyAgreementEnabled, которой этот key share можно было отключить, действовала только до Chrome 146. - Запись в файле hosts. Строка для сайта в
/etc/hostsилиC:\Windows\System32\drivers\etc\hosts, оставшаяся после тестов переезда, отправляет вас на старую машину, которая может вообще не поддерживать TLS. Удалите её. - QUIC. Отключить
chrome://flags/#enable-quic— быстрая проверка, но сбои QUIC обычно выглядят какERR_QUIC_PROTOCOL_ERROR, и Chrome сам переходит на TCP. В Яндекс Браузере служебные страницы открываются черезbrowser://, напримерbrowser://flags. - Устаревшая запись HSTS. Если сайт раньше работал по HTTPS, а потом от него отказался, Chrome всё равно принудительно открывает его по HTTPS. На странице
chrome://net-internals/#hsts(в Edge —edge://net-internals/#hsts) есть поле Delete domain security policies; записи из предзагруженного списка там удалить нельзя.
Владельцу сайта: воспроизведите ошибку снаружи
Проверяйте снаружи и по обоим семействам адресов: сервер может отвечать по TLS на IPv4 и по обычному HTTP на IPv6, и тогда ошибку видит только часть посетителей.
curl -4 -sS -o /dev/null -w '%{http_code}\n' https://example.com/
curl -6 -sS -o /dev/null -w '%{http_code}\n' https://example.com/Если пришёл код статуса, этот путь работает. Подробности покажет OpenSSL, если провести рукопожатие им:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>&1 \
| grep -E 'error|handshake has read'40378B48D87F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:350:
SSL handshake has read 5 bytes and written 317 bytesread 5 bytes — вот признак: OpenSSL прочитал заголовок одной записи, нашёл HTTP/ там, где должен начинаться TLS, и сдался. У рабочего сервера строки с ошибкой нет, а прочитано несколько килобайт, для example.com:443 около 4000 байт.
Без доступа к shell поможет наша проверка SSL (англ.): она выполняет TLS-рукопожатие с портом 443 из Амстердама и, если оно не завершается, показывает ошибку вместо сертификата.
Если проверка вообще не может подключиться, проверка открытых портов (англ.) покажет, доступен ли TCP 443 в принципе; закрытый порт даёт ERR_CONNECTION_REFUSED, а не эту ошибку.
Причина 1: порт 443 отвечает обычным HTTP
nginx работает по TLS только на сокете, у которого в строке listen есть ssl. Без него nginx отдаёт на 443 обычный HTTP, даже если в том же блоке есть строки ssl_certificate, и nginx -t не возражает. Этот блок проходит проверку в nginx 1.22 из Debian 12:
server {
listen 443; # нет "ssl"
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}Чаще всего забывают строку для IPv6: listen [::]:443; рядом с правильной listen 443 ssl; — это отдельный сокет, и посетители по IPv6 получают обычный HTTP. Выведите все строки для 443 без ssl из конфигурации, которую nginx на самом деле загрузил:
sudo nginx -T 2>/dev/null | grep -E '^\s*listen\s+(\S*:)?443\b' | grep -vE 'ssl|quic'Пустой вывод — то, что нужно. В каждую выведенную строку добавьте ssl, затем проверьте конфигурацию и перезагрузите nginx:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/example.com;
}sudo nginx -t && sudo systemctl reload nginxКроме того, nginx оставляет след. Когда браузер отправляет TLS ClientHello на сокет с обычным HTTP, в access-логе появляется «запрос», который начинается с заголовка TLS-записи \x16\x03\x01, со статусом 400:
sudo grep -c '\\x16\\x03\\x01' /var/log/nginx/access.logНесколько совпадений в день — это сканеры; совпадение на каждый заход по HTTPS значит, что какой-то блок на 443 отвечает по обычному HTTP. В других стеках та же ловушка:
- Apache:
<VirtualHost *:443>, скопированный с блока для порта 80, безSSLEngine on. mod_ssl выключен для каждого виртуального хоста, пока эта строка его не включит; на Debian также выполнитеsudo a2enmod ssl. - Docker:
-p 443:80или"443:80"в Compose публикует порт контейнера с обычным HTTP на HTTPS-порту хоста, аdocker psпоказывает это как0.0.0.0:443->80/tcp. Опубликуйте приложение на127.0.0.1:8080:80, а 443 отдайте обратному прокси с сертификатом. - Приложения на Node:
app.listen(443)в Express запускает обычный HTTP-сервер. Используйте модульhttpsили, что лучше, запустите приложение на локальном порту за nginx.
Причина 2: исправление так и не загрузилось
Сертификат и ключ, которые не подходят друг к другу, эту ошибку вызвать не могут: nginx, Node и Python отказываются загружать такую пару. Подвох в другом, и он незаметнее. Вы исправляете строку listen, перезагрузка не проходит, и nginx продолжает работать со старой конфигурацией. Два типичных сообщения nginx -t (nginx 1.22, OpenSSL 3.0, пути для примера):
nginx: [emerg] SSL_CTX_use_PrivateKey("/etc/ssl/private/example.com.key") failed (SSL: error:05800074:x509 certificate routines::key values mismatch)
nginx: configuration file /etc/nginx/nginx.conf test failednginx: [emerg] no "ssl_certificate" is defined for the "listen ... ssl" directive in /etc/nginx/sites-enabled/example.com:1
nginx: configuration file /etc/nginx/nginx.conf test failedНомер строки указывает на начало блока server. Неудачный systemctl reload оставляет работающий nginx нетронутым, а restart с той же ошибкой оставляет nginx остановленным, и вместо ERR_SSL_PROTOCOL_ERROR посетители получат ERR_CONNECTION_REFUSED. Всегда ставьте nginx -t && перед перезагрузкой, а после неё проверяйте снаружи.
Причина 3: отвечает не веб-сервер
Порт 443 открыт даже в строгих сетях, поэтому его часто занимает что-то, что не является HTTPS: SSH, сервер OpenVPN на TCP, мультиплексор вроде sslh с неверным маршрутом по умолчанию или роутер, который пробрасывает 443 не на тот внутренний порт. Настоящий вывод curl для локального тестового сервера, который отвечает баннером OpenSSH:
curl: (35) OpenSSL/3.0.22: error:0A00010B:SSL routines::wrong version numberПосмотрите на сервере, какой процесс занимает порт:
sudo ss -ltnp 'sport = :443'В последней колонке указана программа, например users:(("nginx",…)). Если это не ваш веб-сервер или там пусто, а порт снаружи всё равно отвечает, трафик попадает куда-то ещё: проверьте проброс портов на роутере, целевой порт балансировщика, проброс портов в Docker и не указывают ли записи A и AAAA (dig +short example.com A, затем AAAA) всё ещё на старый сервер.
Причина 4: HSTS отправляет браузер на HTTPS там, где его нет
Как только сайт отправил заголовок Strict-Transport-Security, браузер переписывает каждый запрос http:// к этому хосту на https:// на срок из max-age, а с includeSubDomains — ещё и для каждого внутреннего инструмента на поддомене.
Подвох — в номере порта. Chromium меняет только схему, поэтому http://tools.example.com:8080/ превращается в https://tools.example.com:8080/, и приложение с обычным HTTP на 8080 получает TLS-рукопожатие: ERR_SSL_PROTOCOL_ERROR. В DevTools на вкладке Network эта подмена видна как 307 Internal Redirect с Non-Authoritative-Reason: HSTS. В 443 превращается только порт 80.
Целые домены верхнего уровня внесены в предзагруженный список HSTS в Chrome, среди них .dev, .app и .page: всё, что под ними, работает только по HTTPS. Отдавайте на таком имени TLS или выведите инструмент из-под политики. Быстро отозвать HSTS нельзя: браузеры забывают его, когда истекает max-age или когда получают от этого хоста max-age=0 по корректному HTTPS.
За Cloudflare
Если запись проксируется (оранжевое облако), TLS-рукопожатие браузера заканчивается на Cloudflare, а не на вашем сервере. Если сломан исходный сервер (origin), посетитель видит собственную страницу ошибки Cloudflare — 525 SSL handshake failed или 526 Invalid SSL certificate, — но никогда не ERR_SSL_PROTOCOL_ERROR. Проверьте, какой у вас случай:
dig +short example.com A
dig +short example.com AAAAАдреса Cloudflare (список — на cloudflare.com/ips) значат, что эта ошибка почти всегда на стороне посетителя. Если в ответе IP вашего сервера, запись работает в режиме DNS only (серое облако) или Cloudflare поставлен на паузу.
Именно на этом переключении падают сайты в режиме Flexible: Flexible общается с вашим сервером по обычному HTTP, поэтому origin никогда не нуждался в TLS на 443. При прямом обращении посетители по HTTPS получают эту ошибку или ERR_CONNECTION_REFUSED. Установите на origin настоящий сертификат и переключитесь на Full (strict). Сертификат Cloudflare Origin CA работает только при включённом проксировании, потому что браузеры ему не доверяют.
ERR_SSL_PROTOCOL_ERROR на localhost
localhost sent an invalid response (в русском Chrome — Сайт localhost отправил недействительный ответ.) почти всегда значит, что dev-сервер работает по обычному HTTP, а браузер запросил HTTPS. Настоящий вывод для Python http.server на 127.0.0.1:18443 — от curl и из лога сервера:
curl: (35) OpenSSL/3.0.22: error:0A00010B:SSL routines::wrong version number127.0.0.1 - - [12/Oct/2026 10:09:16] "\x16\x03\x01\x02\x00\x01\x00\x01…" 400 -Почему браузер пошёл по HTTPS:
- Вы так набрали адрес, или так он сохранён в закладке. Откройте
http://localhost:PORT. - Приложение перенаправляет на HTTPS.
config.force_sslв Rails,SECURE_SSL_REDIRECTв Django иUseHttpsRedirection()в ASP.NET Core рассчитаны на продакшен; выключите их в конфигурации для разработки. - HSTS от прошлого проекта. Начиная с версии 132 Chrome игнорирует HSTS для
localhostи*.localhost. Старые версии и собственные имена вродеmyapp.testего сохраняют: удалите запись наchrome://net-internals/#hsts. Локальному имени в.devили.appHTTPS нужен всегда; берите вместо них.testили.localhost.
Если HTTPS нужен при разработке, дайте dev-серверу сертификат. В Next.js есть next dev --experimental-https, который создаёт самоподписанный; mkcert выпускает локально доверенные сертификаты для любого dev-сервера.
Не эта ошибка: проблемы с версией, шифром и сертификатом
Другой код значит, что рукопожатие продвинулось дальше. Сервер только с TLS 1.0 или 1.1 или без шифра, который принимает Chrome, даёт ERR_SSL_VERSION_OR_CIPHER_MISMATCH с надписью «На сайте … используется неподдерживаемый протокол». Просроченный, самоподписанный или выданный на другое имя сертификат даёт код NET::ERR_CERT_* под заголовком «Подключение не защищено» (Your connection is not private).
В Яндекс Браузере у ERR_SSL_VERSION_OR_CIPHER_MISMATCH есть ещё одна частая причина. Согласно справке Браузера, обычно это значит, что сервер поддерживает только TLS с шифрованием по ГОСТ, а поддержка ГОСТ в Браузере выключена. Её включает опция «Подключаться к сайтам, использующим шифрование по ГОСТ. Требуется КриптоПро CSP» в разделе Настройки → Системные → Сеть; КриптоПро CSP должен быть установлен на компьютере.
Чек-лист после любых изменений HTTPS
sudo nginx -tпишетtest is successful, а перезагрузка ничего не вывела.curl -sS -i http://example.com:443/отвергается с кодом 400, аcurl -4иcurl -6кhttps://оба возвращают код статуса — при проверке снаружи сервера.- Порты 80 и 443 открыты в файрволе: как открыть и проверить оба, рассказано в статье порты 443 и 80 (англ.).
На Linux VPS от VMHeaven у вас есть root для каждого шага из этой статьи, а если правка nginx или файрвола отрежет доступ, VNC-консоль в панели, которая открывается прямо в браузере, по-прежнему достаёт до сервера.
Частые вопросы
Ошибка ERR_SSL_PROTOCOL_ERROR — это проблема у меня или у сайта?
Откройте сайт из другой сети, например с телефона через мобильный интернет, или выполните с любого сервера «curl -sv https://example.com/». Если не работает нигде, сломан сайт: обычно на порту 443 отвечает обычный HTTP. Если ошибка только на вашем компьютере или в вашей сети, ищите причину в проверке HTTPS антивирусом, корпоративном файрволе, VPN или прокси.
Как исправить ERR_SSL_PROTOCOL_ERROR в Яндекс Браузере?
Так же, как в Chrome: Яндекс Браузер построен на Chromium и показывает тот же код. Откройте сайт в другом браузере и из другой сети, временно отключите проверку HTTPS в антивирусе, проверьте прокси. Если вместо этого кода Браузер пишет ERR_SSL_VERSION_OR_CIPHER_MISMATCH на сайте с шифрованием по ГОСТ, установите КриптоПро CSP и включите в разделе «Настройки → Системные → Сеть» опцию «Подключаться к сайтам, использующим шифрование по ГОСТ. Требуется КриптоПро CSP».
Помогает ли «Очистить SSL» или очистка кеша при ERR_SSL_PROTOCOL_ERROR?
Редко. Кеш хранит страницы, а не рукопожатия, а кнопка «Очистить SSL» в свойствах браузера Windows затрагивает собственный TLS-стек Windows, а не Chrome или Edge. Помочь может удаление устаревшей записи HSTS на chrome://net-internals/#hsts, если хост больше не отдаёт HTTPS, а если ошибка только у вас — временное отключение проверки защищённых соединений в антивирусе.