Error during WebSocket handshake
Сервер не ответил на запрос Upgrade корректным кодом 101. Что именно не так, сказано в конце сообщения в консоли.
Что это значит
WebSocket начинается с HTTP-запроса с заголовком Upgrade: websocket. Сервер
должен ответить 101 Switching Protocols и прислать заголовки Upgrade,
Connection и Sec-WebSocket-Accept
(RFC 6455, пункт 4.2.2).
Chrome пишет эту ошибку, когда ответ оказался любым другим.
DNS, порт и TLS в порядке. Если бы отказало что-то из них, в сообщении было бы
Error in connection establishment.
JavaScript получает только событие close с кодом 1006 и
пустой причиной. Что именно случилось, написано в консоли, в конце сообщения.
Как исправить
Читайте текст после Error during WebSocket handshake:.
Unexpected response code: NNN. Сервер ответил не кодом 101:
200: обратный прокси потерял заголовокUpgrade, и бэкенд отдал обычную страницу. Либо путь неверный, и одностраничное приложение вернулоindex.html.400,426: обычно тот же потерянный заголовок, только бэкенд отклонил запрос. Socket.IO отвечает400ещё и при несовпадении версий клиента и сервера.403: сервер не принял заголовокOriginили учётные данные. Многие библиотеки по умолчанию пускают только с того же origin, что и сам сервер.404: неверный путь./wsи/ws/могут быть разными маршрутами.301,302: редирект, чаще всего сws://наwss://или на путь со слешем в конце. Браузер за ним не идёт. Указывайте конечный адрес.502,503,504: прокси работает, а приложение за ним не отвечает.
nginx по умолчанию не передаёт Upgrade бэкенду. При 200 и 400 сначала
проверьте эти строки:
location /ws {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Чтобы прочитать ответ целиком, отправьте тот же запрос через curl:
curl -i -N --http1.1 --max-time 5 \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
https://example.com/ws
Не убирайте --http1.1. Без него curl выбирает HTTP/2 везде, где сервер его
поддерживает. В HTTP/2 заголовка Upgrade нет, и сервер отвечает как на обычный
запрос. Чтобы воспроизвести 403, добавьте -H "Origin: https://your-site.example":
браузер отправляет Origin всегда, curl не отправляет.
Incorrect 'Sec-WebSocket-Accept' header value или header is missing.
Значение считается так: SHA-1 от ключа с дописанным фиксированным GUID, затем
base64. Библиотеки считают его верно, поэтому дело в самописном сервере. Чаще всего
ключ декодируют из base64 перед хешированием, отправляют хеш в hex или
опечатываются в GUID. Проверьте значение в
калькуляторе Sec-WebSocket-Accept. Для ключа из команды
curl правильный ответ равен s3pPLMBiTxaQ9kYGzzhZRbK+xOo=.
Сообщение про Upgrade, Connection или Sec-WebSocket-Protocol. Код 101,
но с одним из заголовков что-то не так:
'Upgrade' header is missing,'Connection' header value must contain 'Upgrade': самописный сервер не прислал эти заголовки, или их убрал прокси без поддержки WebSocket.Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received: вы вызвалиnew WebSocket(url, ['v1']), а сервер не выбрал подпротокол. Браузеры требуют, чтобы он вернул одно из предложенных имён. Выберите подпротокол на сервере или уберите второй аргумент.Response must not include 'Sec-WebSocket-Protocol' header if not present in request: сервер вернул подпротокол, которого клиент не просил.
net::ERR_…. Соединение открылось, но ответ оборвался или оказался не HTTP.
Проверьте порт в адресе и лог сервера.
Как увидеть это у себя
Найдите в списке запросов Upgrade к /ws и посмотрите код и заголовки ответа.
Сравните Sec-WebSocket-Key в запросе с Sec-WebSocket-Accept в ответе. Если
Upgrade и Connection в запросе есть, а в ответе 200, их потерял прокси или
путь неверный.
Как Solpuga записывает стрим: Перехват и просмотр трафика.
Инструмент к этой странице
Калькулятор Sec-WebSocket-AcceptИз Sec-WebSocket-Key посчитать ожидаемый Sec-WebSocket-Accept и проверить, что сервер ответил правильно.Рядом
- WebSocket closed with code 1006 — как найти причинуСоединение оборвалось без Close-кадра. Четыре причины и как их различить.
- Стрим обрывается через 60 секунд: proxy_read_timeoutОбрыв на одной и той же секунде означает таймаут, а не сеть. Значения по умолчанию в nginx, ALB, Cloudflare, Heroku и Envoy и почему пинг надёжнее.