WebSocket — это не обычный HTTP-запрос. После первоначального «рукопожатия» (handshake) соединение остаётся открытым, и данные идут в обе стороны непрерывным потоком. Именно поэтому стандартные HTTP-прокси часто обрывают WS-соединения или вообще не умеют их обрабатывать. В этой статье разберём, как правильно проксировать WebSocket-трафик: какие прокси подходят, как их настроить и какие ошибки встречаются чаще всего.
Как работает WebSocket и почему он сложен для прокси
Чтобы понять проблему, нужно разобраться в механике. WebSocket-соединение начинается как обычный HTTP-запрос — клиент отправляет заголовок Upgrade: websocket и Connection: Upgrade. Сервер отвечает кодом 101 Switching Protocols — и с этого момента соединение перестаёт быть HTTP. Оно превращается в постоянный двунаправленный канал, по которому данные идут во фреймах.
Стандартный HTTP/1.1-прокси, который умеет только пересылать запросы и ответы, сталкивается с проблемой: он не знает, что делать с соединением после ответа 101. Многие прокси-серверы просто закрывают соединение в этот момент или возвращают ошибку 502 Bad Gateway. Другие держат соединение, но не умеют корректно передавать WebSocket-фреймы, что приводит к обрывам или искажению данных.
Вот ключевые отличия WebSocket от обычного HTTP с точки зрения проксирования:
| Параметр | HTTP | WebSocket |
|---|---|---|
| Тип соединения | Запрос → Ответ → Закрытие | Постоянное, двунаправленное |
| Время жизни | Секунды (один запрос) | Минуты, часы, дни |
| Инициатор данных | Только клиент | Клиент и сервер |
| Протокол после handshake | HTTP | Собственный (RFC 6455) |
| Порты | 80, 443 | 80 (WS), 443 (WSS) |
Именно из-за смены протокола после handshake большинство простых прокси-решений не справляются с WebSocket. Нужно либо использовать прокси, который явно поддерживает туннелирование, либо применять SOCKS5 — протокол, который работает на более низком уровне и не разбирает содержимое трафика.
Какие типы прокси поддерживают WebSocket
Не все прокси одинаково полезны для WebSocket. Разберём каждый тип:
| Тип прокси | Поддержка WS | Механизм | Сложность |
|---|---|---|---|
| HTTP-прокси | ⚠️ Частично | Через CONNECT-туннель | Средняя |
| HTTPS-прокси | ✅ Да | CONNECT + TLS | Средняя |
| SOCKS4 | ⚠️ Ограниченно | TCP-туннель без auth | Низкая |
| SOCKS5 | ✅ Полная | Прозрачный TCP/UDP | Низкая |
| Transparent proxy | ❌ Нет | Только HTTP | — |
Вывод: для WebSocket-задач оптимальным выбором является SOCKS5. Этот протокол работает на транспортном уровне и просто туннелирует TCP-соединение, не вникая в содержимое трафика. Ему всё равно, идёт ли внутри HTTP, WebSocket, SSH или что-то ещё. HTTP-прокси тоже могут работать с WS, но только через метод CONNECT — и здесь есть нюансы, которые разберём ниже.
Метод CONNECT: как HTTP-прокси туннелирует WebSocket
HTTP-метод CONNECT — это специальный механизм, который позволяет HTTP-прокси создавать «слепой» туннель до целевого сервера. Прокси не анализирует трафик внутри туннеля, а просто перенаправляет байты. Именно так работает HTTPS через HTTP-прокси — и именно так можно проксировать WebSocket.
Процесс выглядит следующим образом:
- Клиент отправляет прокси запрос:
CONNECT example.com:443 HTTP/1.1 - Прокси устанавливает TCP-соединение с
example.com:443 - Прокси отвечает клиенту:
200 Connection Established - С этого момента прокси просто передаёт байты туда и обратно — не разбирая их
- Клиент выполняет TLS-handshake напрямую с сервером через туннель
- Затем — WebSocket-handshake поверх TLS
Ключевое ограничение: метод CONNECT обычно разрешён только для порта 443. Если ваш WebSocket-сервер работает на нестандартном порту (например, 8080 или 9000), прокси может отклонить соединение. В этом случае SOCKS5 предпочтительнее — он не имеет ограничений по портам.
Также важно учитывать, что некоторые корпоративные HTTP-прокси (например, Squid в стандартной конфигурации) явно блокируют метод CONNECT для определённых портов или требуют аутентификацию. Если вы работаете с коммерческими прокси-провайдерами, большинство из них поддерживают CONNECT без ограничений.
# Пример CONNECT-запроса через curl (для проверки)
curl -v -x http://proxy_host:proxy_port \
--proxytunnel \
https://echo.websocket.org
# Если прокси поддерживает CONNECT — вы увидите:
# * CONNECT tunnel established, response 200
SOCKS5 и WebSocket: почему это лучший вариант
SOCKS5 — это протокол проксирования на уровне TCP/UDP-соединений. В отличие от HTTP-прокси, SOCKS5 не знает ничего о прикладном протоколе, который идёт внутри. Он просто создаёт туннель между клиентом и целевым сервером, и всё. Это делает его идеальным для WebSocket по нескольким причинам:
- Нет ограничений по протоколу: SOCKS5 туннелирует любой TCP-трафик, включая WS, WSS, SSH, FTP и т.д.
- Нет ограничений по портам: работает с любым портом, не только 443 или 80
- Нет разрыва при смене протокола: прокси не «видит» переход от HTTP к WebSocket
- Поддержка аутентификации: SOCKS5 поддерживает логин/пароль, что удобно для коммерческих прокси
- Поддержка UDP: если ваше приложение использует WebRTC или UDP рядом с WS — SOCKS5 справится
Практически все современные библиотеки для работы с WebSocket поддерживают SOCKS5 либо напрямую, либо через дополнительные пакеты. Ниже рассмотрим конкретные примеры для Python и Node.js.
💡 Когда выбирать SOCKS5, а когда HTTP CONNECT?
Используйте SOCKS5, если: нестандартный порт, нужна поддержка UDP, хотите минимум настроек.
Используйте HTTP CONNECT, если: прокси-провайдер не поддерживает SOCKS5, или вы работаете через корпоративный прокси.
Примеры кода: WebSocket через прокси на Python
Рассмотрим несколько сценариев для Python. Самые популярные библиотеки для WebSocket в Python — это websockets и websocket-client.
Вариант 1: websocket-client через HTTP-прокси
import websocket
# Настройки HTTP-прокси
proxy_host = "proxy.example.com"
proxy_port = 8080
proxy_user = "username"
proxy_pass = "password"
ws = websocket.WebSocket()
ws.connect(
"wss://echo.websocket.org",
http_proxy_host=proxy_host,
http_proxy_port=proxy_port,
http_proxy_auth=(proxy_user, proxy_pass),
proxy_type="http" # или "socks5"
)
ws.send("Hello, WebSocket!")
result = ws.recv()
print(f"Получено: {result}")
ws.close()
Вариант 2: websocket-client через SOCKS5
import websocket
# Для SOCKS5 нужен пакет: pip install PySocks
ws = websocket.WebSocket()
ws.connect(
"wss://echo.websocket.org",
http_proxy_host="socks5_proxy.example.com",
http_proxy_port=1080,
http_proxy_auth=("username", "password"),
proxy_type="socks5"
)
ws.send("Test message")
print(ws.recv())
ws.close()
Вариант 3: библиотека websockets (asyncio) через SOCKS5
Библиотека websockets (asyncio) не имеет встроенной поддержки прокси, поэтому используем python-socks для создания туннеля:
# pip install websockets python-socks[asyncio]
import asyncio
import websockets
from python_socks.async_.asyncio import Proxy
async def connect_via_socks5():
proxy = Proxy.from_url("socks5://username:[email protected]:1080")
# Создаём TCP-соединение через прокси
sock = await proxy.connect(
dest_host="echo.websocket.org",
dest_port=443
)
# Передаём сокет в websockets
async with websockets.connect(
"wss://echo.websocket.org",
sock=sock
) as ws:
await ws.send("Hello via SOCKS5!")
response = await ws.recv()
print(f"Ответ: {response}")
asyncio.run(connect_via_socks5())
Вариант 4: глобальный патч через PySocks
Если вы хотите направить весь трафик Python-приложения через SOCKS5 без изменения каждого вызова — используйте socks.setdefaultproxy():
# pip install PySocks
import socks
import socket
import websocket
# Патчим socket глобально
socks.set_default_proxy(
socks.SOCKS5,
"proxy.example.com",
1080,
username="user",
password="pass"
)
socket.socket = socks.socksocket
# Теперь все соединения идут через SOCKS5
ws = websocket.WebSocket()
ws.connect("wss://echo.websocket.org")
ws.send("Global SOCKS5 proxy!")
print(ws.recv())
ws.close()
Примеры кода: WebSocket через прокси на Node.js
В экосистеме Node.js наиболее популярная библиотека для WebSocket — ws. Для проксирования через HTTP/SOCKS5 используется пакет https-proxy-agent или socks-proxy-agent.
Вариант 1: WSS через HTTP CONNECT прокси
// npm install ws https-proxy-agent
const WebSocket = require('ws');
const { HttpsProxyAgent } = require('https-proxy-agent');
const proxyUrl = 'http://username:[email protected]:8080';
const agent = new HttpsProxyAgent(proxyUrl);
const ws = new WebSocket('wss://echo.websocket.org', { agent });
ws.on('open', () => {
console.log('Соединение установлено через HTTP-прокси');
ws.send('Hello from Node.js!');
});
ws.on('message', (data) => {
console.log(`Получено: ${data}`);
ws.close();
});
ws.on('error', (err) => {
console.error('Ошибка:', err.message);
});
Вариант 2: WSS через SOCKS5
// npm install ws socks-proxy-agent
const WebSocket = require('ws');
const { SocksProxyAgent } = require('socks-proxy-agent');
const proxyUrl = 'socks5://username:[email protected]:1080';
const agent = new SocksProxyAgent(proxyUrl);
const ws = new WebSocket('wss://echo.websocket.org', { agent });
ws.on('open', () => {
console.log('Соединение через SOCKS5 установлено');
ws.send(JSON.stringify({ type: 'ping', data: 'test' }));
});
ws.on('message', (data) => {
console.log('Ответ:', data.toString());
});
ws.on('close', (code, reason) => {
console.log(`Закрыто: ${code} - ${reason}`);
});
Вариант 3: WS (без TLS) через HTTP-прокси вручную
Для нешифрованного WS (порт 80) через HTTP-прокси нужно вручную отправить CONNECT-запрос, так как стандартные агенты часто работают только с HTTPS:
const net = require('net');
const WebSocket = require('ws');
function createTunnel(proxyHost, proxyPort, targetHost, targetPort) {
return new Promise((resolve, reject) => {
const socket = net.connect(proxyPort, proxyHost, () => {
const connectReq =
`CONNECT ${targetHost}:${targetPort} HTTP/1.1\r\n` +
`Host: ${targetHost}:${targetPort}\r\n` +
`Proxy-Authorization: Basic ${Buffer.from('user:pass').toString('base64')}\r\n` +
`\r\n`;
socket.write(connectReq);
});
socket.once('data', (data) => {
if (data.toString().includes('200')) {
resolve(socket);
} else {
reject(new Error(`Прокси отклонил CONNECT: ${data.toString()}`));
}
});
socket.on('error', reject);
});
}
async function main() {
const socket = await createTunnel(
'proxy.example.com', 8080,
'echo.websocket.org', 80
);
const ws = new WebSocket('ws://echo.websocket.org', { socket });
ws.on('open', () => {
ws.send('Manual CONNECT tunnel!');
});
ws.on('message', (data) => {
console.log('Получено:', data.toString());
ws.close();
});
}
main().catch(console.error);
WSS (WebSocket Secure): особенности проксирования с TLS
WSS — это WebSocket поверх TLS (аналогично HTTPS для HTTP). При проксировании WSS через SOCKS5 или HTTP CONNECT возникает важный нюанс: TLS-шифрование устанавливается между клиентом и конечным сервером, а не между клиентом и прокси. Это означает:
- Прокси-сервер не видит содержимое WSS-трафика — только IP-адрес и порт назначения
- Сертификат сервера проверяется клиентом напрямую
- Прокси не может «подменить» или «перехватить» данные без установки собственного CA-сертификата
Это хорошая новость с точки зрения безопасности. Но есть и практические нюансы при настройке:
Проверка сертификата через прокси
Иногда при использовании корпоративных прокси (которые делают SSL-инспекцию) вы можете получить ошибку проверки сертификата. В этом случае прокси подменяет сертификат сервера своим. Для работы в таком окружении нужно добавить CA-сертификат прокси в доверенные:
# Python: передаём CA-сертификат корпоративного прокси
import ssl
import websocket
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations("/path/to/corporate-ca.crt")
ws = websocket.WebSocket(sslopt={"context": ssl_context})
ws.connect(
"wss://internal.example.com",
http_proxy_host="corp-proxy.company.com",
http_proxy_port=8080,
proxy_type="http"
)
# В тестовых средах (НЕ для продакшена!) можно отключить проверку:
ws_test = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE})
ws_test.connect("wss://test.example.com", http_proxy_host="proxy", http_proxy_port=8080)
⚠️ Важно
Отключение проверки TLS-сертификата (CERT_NONE) допустимо только в тестовой среде. В продакшене это создаёт уязвимость для атак типа MITM (man-in-the-middle).
SNI (Server Name Indication) через прокси
При использовании SOCKS5 клиент может самостоятельно разрешать DNS или делегировать это прокси. Режим socks5h (SOCKS5 with hostname resolution) означает, что DNS-запрос выполняется на стороне прокси-сервера. Это важно для WSS, так как SNI-заголовок в TLS-handshake должен совпадать с именем хоста:
# socks5 — DNS разрешается локально (клиентом)
# socks5h — DNS разрешается прокси-сервером (рекомендуется для анонимности)
from python_socks.async_.asyncio import Proxy
# DNS через прокси (рекомендуется):
proxy = Proxy.from_url("socks5h://user:[email protected]:1080")
# DNS локально:
proxy = Proxy.from_url("socks5://user:[email protected]:1080")
Типичные ошибки и как их исправить
Собрали самые частые проблемы при проксировании WebSocket с решениями:
Ошибка 1: 407 Proxy Authentication Required
Прокси требует аутентификацию, но вы не передали учётные данные или передали их неверно.
# ❌ Неверно — нет авторизации
ws.connect("wss://example.com", http_proxy_host="proxy.example.com", http_proxy_port=8080)
# ✅ Верно — передаём логин и пароль
ws.connect(
"wss://example.com",
http_proxy_host="proxy.example.com",
http_proxy_port=8080,
http_proxy_auth=("username", "password")
)
Ошибка 2: Connection reset by peer / 502 Bad Gateway
Прокси не поддерживает WebSocket или метод CONNECT. Решение: переключитесь на SOCKS5 или проверьте, поддерживает ли ваш провайдер WebSocket-трафик.
Ошибка 3: Соединение обрывается через 30-60 секунд
Многие прокси-серверы закрывают «неактивные» TCP-соединения по таймауту. WebSocket-соединение может выглядеть неактивным, если нет обмена данными. Решение — включить ping/pong:
# Python — включаем keepalive ping каждые 20 секунд
import websocket
import threading
def run():
ws = websocket.WebSocketApp(
"wss://echo.websocket.org",
on_message=lambda ws, msg: print(msg),
on_error=lambda ws, err: print(f"Ошибка: {err}"),
on_close=lambda ws, c, m: print("Закрыто")
)
ws.run_forever(
ping_interval=20, # отправлять ping каждые 20 сек
ping_timeout=10, # ждать pong не более 10 сек
http_proxy_host="proxy.example.com",
http_proxy_port=8080,
proxy_type="socks5"
)
thread = threading.Thread(target=run)
thread.start()
Ошибка 4: SSL: CERTIFICATE_VERIFY_FAILED
Чаще всего возникает при использовании корпоративного прокси с SSL-инспекцией. Решение: добавьте CA-сертификат прокси в доверенные (см. раздел выше) или используйте SOCKS5 вместо HTTP-прокси — SOCKS5 не делает SSL-инспекцию.
Ошибка 5: Handshake status 403 Forbidden
Целевой сервер блокирует соединение. Причины: IP прокси занесён в чёрный список, отсутствуют нужные заголовки (Origin, User-Agent), или сервер блокирует трафик из дата-центров. Решение: используйте резидентные прокси с реальными IP домашних пользователей — их значительно сложнее заблокировать.
Ошибка 6: [Errno 111] Connection refused
Прокси-сервер недоступен: неверный хост/порт, или прокси не запущен. Проверьте данные подключения и доступность прокси через простой HTTP-запрос перед тестированием WebSocket.
Какой тип прокси выбрать для WebSocket-задач
Выбор типа прокси зависит от конкретной задачи. Вот практическое руководство:
| Задача | Рекомендуемый тип | Почему |
|---|---|---|
| Парсинг через WS (биржи, финансовые данные) | Прокси дата-центров | Высокая скорость, низкая задержка, стабильное соединение |
| Обход блокировок WebSocket-сервисов | Резидентные прокси | Реальные IP, минимальный риск блокировки |
| Мобильные приложения с WS (работа с мобильными API) | Мобильные прокси | IP мобильных операторов — высокое доверие со стороны сервисов |
| Нагрузочное тестирование WS-сервера | Прокси дата-центров | Дёшево, быстро, много одновременных соединений |
| Геолокационное тестирование WS | Резидентные прокси | Большой выбор стран и городов |
Важные параметры прокси для WebSocket
При выборе прокси-провайдера для WebSocket-задач обращайте внимание на следующие параметры:
- Поддержка SOCKS5: убедитесь, что провайдер предоставляет SOCKS5, а не только HTTP
- Время сессии (session duration): для WebSocket важны «sticky» сессии — один IP на длительное время. Ротируемые прокси будут обрывать соединение
- Таймаут соединения: прокси должен поддерживать долгоживущие TCP-соединения (от нескольких минут до часов)
- Пропускная способность: для потокового WebSocket (видео, биржевые данные) важна высокая пропускная способность без ограничений
- Задержка (latency): для финансовых приложений и торговых ботов критична минимальная задержка — выбирайте прокси с серверами ближе к целевому сервису
💡 Проверка поддержки WebSocket прокси-провайдером
Прежде чем покупать прокси для WebSocket-задач, протестируйте их через бесплатный эхо-сервер:
wss://echo.websocket.org или
wss://ws.postman-echo.com/raw.
Если соединение устанавливается и сообщения возвращаются — прокси работает с WebSocket корректно.
Настройка sticky-сессии для WebSocket
Большинство провайдеров резидентных прокси используют ротацию IP по умолчанию. Для WebSocket это неприемлемо — каждая смена IP означает разрыв соединения. Убедитесь, что вы используете режим sticky session (фиксированный IP). Обычно это делается через специальный формат URL прокси:
# Пример формата sticky-сессии (зависит от провайдера):
# Ротируемый (НЕ подходит для WebSocket):
socks5://user:[email protected]:1080
# Sticky-сессия (подходит для WebSocket):
socks5://user-session-abc123:[email protected]:1080
# Или через параметр страны и сессии:
socks5://user-country-us-session-12345:[email protected]:1080
Заключение
WebSocket — это не просто «HTTP с долгим соединением». Это отдельный протокол, который требует особого подхода при проксировании. Главные выводы из этой статьи:
- SOCKS5 — оптимальный выбор для WebSocket: работает на транспортном уровне, не разбирает протокол, поддерживает любые порты
- HTTP-прокси через CONNECT тоже работает, но с ограничениями по портам и возможными проблемами с SSL-инспекцией
- Sticky-сессии обязательны: ротируемые прокси будут обрывать WebSocket-соединение при каждой смене IP
- Ping/pong keepalive необходим для предотвращения разрыва соединения по таймауту прокси
- WSS через прокси безопасен: TLS-шифрование устанавливается напрямую между клиентом и сервером, прокси не видит содержимое
Если вы разрабатываете приложение, которое работает с WebSocket через прокси — начните с SOCKS5 и sticky-сессий. Это сэкономит вам часы отладки. Для задач, где важна высокая скорость и стабильность соединения (торговые боты, стриминг данных), отлично подойдут прокси дата-центров с низкой задержкой. Если же целевой сервис активно блокирует дата-центровые IP — рассмотрите резидентные прокси: они имеют реальные IP домашних пользователей и значительно реже попадают под блокировки даже при длительных WebSocket-сессиях.