WebSocket is not a regular HTTP request. After the initial handshake, the connection remains open, and data flows in both directions continuously. This is why standard HTTP proxies often break WS connections or cannot handle them at all. In this article, we will discuss how to properly proxy WebSocket traffic: which proxies are suitable, how to configure them, and what common errors occur.
How WebSocket Works and Why Itβs Challenging for Proxies
To understand the problem, we need to delve into the mechanics. A WebSocket connection starts as a regular HTTP request β the client sends the headers Upgrade: websocket and Connection: Upgrade. The server responds with a 101 Switching Protocols code β and from this moment, the connection ceases to be HTTP. It turns into a persistent bidirectional channel through which data flows in frames.
A standard HTTP/1.1 proxy, which can only forward requests and responses, faces a problem: it does not know what to do with the connection after the 101 response. Many proxy servers simply close the connection at this point or return a 502 Bad Gateway error. Others keep the connection open but cannot properly forward WebSocket frames, resulting in disconnections or data corruption.
Here are the key differences between WebSocket and regular HTTP from a proxying perspective:
| Parameter | HTTP | WebSocket |
|---|---|---|
| Connection Type | Request β Response β Close | Persistent, Bidirectional |
| Lifetime | Seconds (one request) | Minutes, Hours, Days |
| Data Initiator | Only Client | Client and Server |
| Protocol After Handshake | HTTP | Custom (RFC 6455) |
| Ports | 80, 443 | 80 (WS), 443 (WSS) |
It is precisely because of the protocol switch after the handshake that most simple proxy solutions struggle with WebSocket. You either need to use a proxy that explicitly supports tunneling or use SOCKS5 β a protocol that operates at a lower level and does not inspect the traffic content.
Which Types of Proxies Support WebSocket
Not all proxies are equally useful for WebSocket. Let's examine each type:
| Proxy Type | WS Support | Mechanism | Complexity |
|---|---|---|---|
| HTTP Proxy | β οΈ Partially | Via CONNECT Tunnel | Medium |
| HTTPS Proxy | β Yes | CONNECT + TLS | Medium |
| SOCKS4 | β οΈ Limited | TCP Tunnel without Auth | Low |
| SOCKS5 | β Full | Transparent TCP/UDP | Low |
| Transparent Proxy | β No | Only HTTP | β |
Conclusion: for WebSocket tasks, SOCKS5 is the optimal choice. This protocol operates at the transport layer and simply tunnels the TCP connection without inspecting the traffic content. It does not care whether HTTP, WebSocket, SSH, or anything else is inside. HTTP proxies can also work with WS, but only through the CONNECT method β and there are nuances that we will discuss below.
CONNECT Method: How HTTP Proxies Tunnel WebSocket
The HTTP method CONNECT is a special mechanism that allows HTTP proxies to create a "blind" tunnel to the target server. The proxy does not analyze the traffic inside the tunnel; it simply redirects bytes. This is how HTTPS works through HTTP proxies β and this is how WebSocket can be proxied.
The process looks like this:
- The client sends a request to the proxy:
CONNECT example.com:443 HTTP/1.1 - The proxy establishes a TCP connection with
example.com:443 - The proxy responds to the client:
200 Connection Established - From this point on, the proxy simply forwards bytes back and forth β without inspecting them
- The client performs a TLS handshake directly with the server through the tunnel
- Then β WebSocket handshake over TLS
A key limitation: the CONNECT method is usually allowed only for port 443. If your WebSocket server operates on a non-standard port (e.g., 8080 or 9000), the proxy may reject the connection. In this case, SOCKS5 is preferable β it has no port restrictions.
It is also important to note that some corporate HTTP proxies (e.g., Squid in the default configuration) explicitly block the CONNECT method for certain ports or require authentication. If you are working with commercial proxy providers, most of them support CONNECT without restrictions.
# Example CONNECT request using curl (for testing)
curl -v -x http://proxy_host:proxy_port \
--proxytunnel \
https://echo.websocket.org
# If the proxy supports CONNECT β you will see:
# * CONNECT tunnel established, response 200
SOCKS5 and WebSocket: Why Itβs the Best Option
SOCKS5 is a proxy protocol at the TCP/UDP connection level. Unlike HTTP proxies, SOCKS5 knows nothing about the application protocol that is going inside. It simply creates a tunnel between the client and the target server, and thatβs it. This makes it ideal for WebSocket for several reasons:
- No protocol restrictions: SOCKS5 tunnels any TCP traffic, including WS, WSS, SSH, FTP, etc.
- No port restrictions: works with any port, not just 443 or 80
- No disconnection when switching protocols: the proxy does not "see" the transition from HTTP to WebSocket
- Support for authentication: SOCKS5 supports username/password, which is convenient for commercial proxies
- UDP support: if your application uses WebRTC or UDP alongside WS β SOCKS5 can handle it
Almost all modern libraries for working with WebSocket support SOCKS5 either directly or through additional packages. Below we will look at specific examples for Python and Node.js.
π‘ When to Choose SOCKS5 and When to Use HTTP CONNECT?
Use SOCKS5 if: non-standard port, need UDP support, want minimal configuration.
Use HTTP CONNECT if: the proxy provider does not support SOCKS5, or you are working through a corporate proxy.
Code Examples: WebSocket through Proxy in Python
Let's consider several scenarios for Python. The most popular libraries for WebSocket in Python are websockets and websocket-client.
Option 1: websocket-client through HTTP Proxy
import websocket
# HTTP Proxy settings
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" # or "socks5"
)
ws.send("Hello, WebSocket!")
result = ws.recv()
print(f"Received: {result}")
ws.close()
Option 2: websocket-client through SOCKS5
import websocket
# For SOCKS5, you need the package: 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()
Option 3: websockets library (asyncio) through SOCKS5
The websockets (asyncio) library does not have built-in proxy support, so we use python-socks to create the tunnel:
# 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")
# Create TCP connection through the proxy
sock = await proxy.connect(
dest_host="echo.websocket.org",
dest_port=443
)
# Pass the socket to 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: {response}")
asyncio.run(connect_via_socks5())
Option 4: Global Patch via PySocks
If you want to route all traffic from your Python application through SOCKS5 without changing each call β use socks.setdefaultproxy():
# pip install PySocks
import socks
import socket
import websocket
# Globally patch the socket
socks.set_default_proxy(
socks.SOCKS5,
"proxy.example.com",
1080,
username="user",
password="pass"
)
socket.socket = socks.socksocket
# Now all connections go through SOCKS5
ws = websocket.WebSocket()
ws.connect("wss://echo.websocket.org")
ws.send("Global SOCKS5 proxy!")
print(ws.recv())
ws.close()
Code Examples: WebSocket through Proxy in Node.js
In the Node.js ecosystem, the most popular library for WebSocket is ws. For proxying through HTTP/SOCKS5, the https-proxy-agent or socks-proxy-agent package is used.
Option 1: WSS through HTTP CONNECT Proxy
// 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('Connection established through HTTP proxy');
ws.send('Hello from Node.js!');
});
ws.on('message', (data) => {
console.log(`Received: ${data}`);
ws.close();
});
ws.on('error', (err) => {
console.error('Error:', err.message);
});
Option 2: WSS through 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('Connection established through SOCKS5');
ws.send(JSON.stringify({ type: 'ping', data: 'test' }));
});
ws.on('message', (data) => {
console.log('Response:', data.toString());
});
ws.on('close', (code, reason) => {
console.log(`Closed: ${code} - ${reason}`);
});
Option 3: WS (without TLS) through HTTP Proxy Manually
For unencrypted WS (port 80) through an HTTP proxy, you need to manually send a CONNECT request, as standard agents often only work with 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(`Proxy rejected 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('Received:', data.toString());
ws.close();
});
}
main().catch(console.error);
WSS (WebSocket Secure): Features of Proxying with TLS
WSS is WebSocket over TLS (similar to HTTPS for HTTP). When proxying WSS through SOCKS5 or HTTP CONNECT, there is an important nuance: TLS encryption is established between the client and the end server, not between the client and the proxy. This means:
- The proxy server does not see the content of WSS traffic β only the destination IP address and port
- The server certificate is verified directly by the client
- The proxy cannot "replace" or "intercept" data without installing its own CA certificate
This is good news from a security perspective. But there are also practical nuances when configuring:
Certificate Verification through Proxy
Sometimes when using corporate proxies (which perform SSL inspection), you may encounter certificate verification errors. In this case, the proxy replaces the server certificate with its own. To work in such an environment, you need to add the proxy's CA certificate to the trusted ones:
# Python: passing the corporate proxy's CA certificate
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"
)
# In testing environments (NOT for production!) you can disable verification:
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)
β οΈ Important
Disabling TLS certificate verification (CERT_NONE) is acceptable only in a testing environment. In production, this creates a vulnerability to MITM (man-in-the-middle) attacks.
SNI (Server Name Indication) through Proxy
When using SOCKS5, the client can resolve DNS on its own or delegate this to the proxy. The socks5h mode (SOCKS5 with hostname resolution) means that the DNS request is performed on the proxy server side. This is important for WSS, as the SNI header in the TLS handshake must match the hostname:
# socks5 β DNS is resolved locally (by the client)
# socks5h β DNS is resolved by the proxy server (recommended for anonymity)
from python_socks.async_.asyncio import Proxy
# DNS through proxy (recommended):
proxy = Proxy.from_url("socks5h://user:[email protected]:1080")
# DNS locally:
proxy = Proxy.from_url("socks5://user:[email protected]:1080")
Common Errors and How to Fix Them
We have compiled the most common issues when proxying WebSocket along with solutions:
Error 1: 407 Proxy Authentication Required
The proxy requires authentication, but you did not provide credentials or provided them incorrectly.
# β Incorrect β no authorization
ws.connect("wss://example.com", http_proxy_host="proxy.example.com", http_proxy_port=8080)
# β
Correct β providing username and password
ws.connect(
"wss://example.com",
http_proxy_host="proxy.example.com",
http_proxy_port=8080,
http_proxy_auth=("username", "password")
)
Error 2: Connection reset by peer / 502 Bad Gateway
The proxy does not support WebSocket or the CONNECT method. Solution: switch to SOCKS5 or check if your provider supports WebSocket traffic.
Error 3: Connection drops after 30-60 seconds
Many proxy servers close "inactive" TCP connections due to timeout. A WebSocket connection may appear inactive if there is no data exchange. The solution is to enable ping/pong:
# Python β enable keepalive ping every 20 seconds
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"Error: {err}"),
on_close=lambda ws, c, m: print("Closed")
)
ws.run_forever(
ping_interval=20, # send ping every 20 seconds
ping_timeout=10, # wait for pong no more than 10 seconds
http_proxy_host="proxy.example.com",
http_proxy_port=8080,
proxy_type="socks5"
)
thread = threading.Thread(target=run)
thread.start()
Error 4: SSL: CERTIFICATE_VERIFY_FAILED
This often occurs when using a corporate proxy with SSL inspection. Solution: add the proxy's CA certificate to the trusted ones (see the section above) or use SOCKS5 instead of HTTP proxy β SOCKS5 does not perform SSL inspection.
Error 5: Handshake status 403 Forbidden
The target server blocks the connection. Reasons: the proxy's IP is blacklisted, required headers (Origin, User-Agent) are missing, or the server blocks traffic from data centers. Solution: use residential proxies with real home user IPs β they are significantly harder to block.
Error 6: [Errno 111] Connection refused
The proxy server is unavailable: incorrect host/port, or the proxy is not running. Check the connection data and the availability of the proxy with a simple HTTP request before testing WebSocket.
Which Type of Proxy to Choose for WebSocket Tasks
The choice of proxy type depends on the specific task. Hereβs a practical guide:
| Task | Recommended Type | Why |
|---|---|---|
| Parsing via WS (exchanges, financial data) | Data Center Proxies | High speed, low latency, stable connection |
| Bypassing WebSocket service blocks | Residential Proxies | Real IPs, minimal risk of blocking |
| Mobile applications with WS (working with mobile APIs) | Mobile Proxies | IP from mobile operators β high trust from services |
| Load testing WS server | Data Center Proxies | Cheap, fast, many simultaneous connections |
| Geolocation testing WS | Residential Proxies | Wide selection of countries and cities |
Important Proxy Parameters for WebSocket
When choosing a proxy provider for WebSocket tasks, pay attention to the following parameters:
- SOCKS5 Support: ensure that the provider offers SOCKS5, not just HTTP
- Session Duration: for WebSocket, "sticky" sessions are important β one IP for a long time. Rotating proxies will drop the connection
- Connection Timeout: the proxy should support long-lived TCP connections (from several minutes to hours)
- Bandwidth: for streaming WebSocket (video, exchange data), high bandwidth without limits is important
- Latency: for financial applications and trading bots, minimal latency is critical β choose proxies with servers closer to the target service
π‘ Checking WebSocket Support by Proxy Provider
Before purchasing proxies for WebSocket tasks, test them through a free echo server:
wss://echo.websocket.org or
wss://ws.postman-echo.com/raw.
If the connection is established and messages are returned β the proxy works with WebSocket correctly.
Configuring Sticky Session for WebSocket
Most residential proxy providers use IP rotation by default. For WebSocket, this is unacceptable β each IP change means a connection drop. Ensure you are using sticky session mode (fixed IP). This is usually done through a special proxy URL format:
# Example of sticky session format (depends on the provider):
# Rotating (NOT suitable for WebSocket):
socks5://user:[email protected]:1080
# Sticky session (suitable for WebSocket):
socks5://user-session-abc123:[email protected]:1080
# Or through country and session parameter:
socks5://user-country-us-session-12345:[email protected]:1080
Conclusion
WebSocket is not just "HTTP with a long connection." It is a separate protocol that requires a special approach when proxying. The main takeaways from this article are:
- SOCKS5 is the optimal choice for WebSocket: it operates at the transport level, does not inspect the protocol, supports any ports
- HTTP proxies via CONNECT also work, but with port limitations and potential issues with SSL inspection
- Sticky sessions are mandatory: rotating proxies will drop the WebSocket connection with each IP change
- Ping/pong keepalive is necessary to prevent connection drops due to proxy timeout
- WSS through proxy is secure: TLS encryption is established directly between the client and server, the proxy does not see the content
If you are developing an application that works with WebSocket through a proxy β start with SOCKS5 and sticky sessions. This will save you hours of debugging. For tasks where high speed and connection stability are important (trading bots, data streaming), data center proxies with low latency are excellent. However, if the target service actively blocks data center IPs β consider residential proxies: they have real home user IPs and are significantly less likely to be blocked even during long WebSocket sessions.