When working with proxies, developers often encounter the issue that classic HTTP proxy settings do not work for WebSocket connections, and gRPC fails altogether with TLS handshake errors. The problem is that WebSocket, HTTP/2, and gRPC are not just "variations of HTTP," but different transport models with their own requirements for tunneling, multiplexing, and header handling. In this article, we will discuss how to proxy each of these protocols, the pitfalls encountered in practice, and how to choose the protocol and type of proxy for a specific task — from parsing via WebSocket to high-load gRPC microservices.
How protocols differ and why it matters for proxies
HTTP/1.1 operates on a "request-response" model: the client opens a connection, sends a request, receives a response, and either closes the connection or keeps it open for the next request (keep-alive). Proxy servers have been optimized for this model for decades — parsing headers, buffering request bodies, simple routing based on the Host header.
WebSocket breaks this model: after the initial HTTP handshake (Upgrade: websocket), the connection becomes a persistent bidirectional channel, where data can flow in both directions at any time. The proxy must "release" this connection and simply forward bytes in both directions, without trying to interpret them as HTTP requests.
HTTP/2 adds multiplexing — multiple logical requests go through a single TCP connection in parallel, using binary frames instead of text headers. For proxies, this means that you cannot just read text line by line — support for the binary HTTP/2 protocol at the proxy level or transparent TCP tunneling over TLS is required.
gRPC goes even further: it is strictly built on HTTP/2, uses Protocol Buffers for serialization, and requires HTTP/2-compatible transport all the way from the client to the server. If a proxy at some point "downgrades" the connection to HTTP/1.1, the gRPC request simply will not pass.
Understanding these differences is critical because choosing the wrong type of proxy or incorrect tunneling configuration leads to connection drops, timeouts, and hard-to-explain errors that are difficult to diagnose without understanding the transport layer.
WebSocket through proxy: setup and code
For WebSocket through a proxy, there are two main scenarios: HTTP proxy with the CONNECT method (for WSS, i.e., WebSocket over TLS) and SOCKS5 proxy, which tunnels the TCP connection without protocol interpretation. SOCKS5 usually causes fewer problems since it was originally designed as a "transparent pipe" for any TCP traffic.
Here is an example of connecting to WebSocket through a SOCKS5 proxy in Python using the websockets
and python-socks libraries:
import asyncio
from python_socks.sync import Proxy
import websockets
import websockets.sync.client as ws_client
def connect_ws_via_proxy():
proxy = Proxy.from_url("socks5://user:pass@proxy_host:1080")
sock = proxy.connect(dest_host="echo.websocket.events", dest_port=443)
ws = ws_client.connect(
"wss://echo.websocket.events",
sock=sock,
server_hostname="echo.websocket.events"
)
ws.send("Hello via proxy")
print(ws.recv())
ws.close()
connect_ws_via_proxy()
In Node.js, a similar task is solved using the ws
package in conjunction with socks-proxy-agent:
const WebSocket = require('ws');
const { SocksProxyAgent } = require('socks-proxy-agent');
const agent = new SocksProxyAgent('socks5://user:pass@proxy_host:1080');
const socket = new WebSocket('wss://echo.websocket.events', { agent });
socket.on('open', () => socket.send('Hello via proxy'));
socket.on('message', (data) => console.log(data.toString()));
If the proxy is only available via HTTP (with the CONNECT method), the scheme is similar — most modern WebSocket clients can work through an HTTP tunnel; it is important to ensure that the proxy does not drop long-lived connections due to timeouts. This is a common issue with cheap data center proxies, which have a strict limit of 30-60 seconds on inactive connections — for streaming WebSocket tasks, this is critical, and it is better to clarify keep-alive parameters with the provider.
HTTP/2 through proxy: multiplexing and pitfalls
The main difficulty with HTTP/2 through proxies is that many proxy servers (especially older Squid or simple forward proxies) only operate on HTTP/1.1 and automatically "downgrade" the connection. For the client, this is often unnoticed (the request still executes), but the benefits of multiplexing are lost — latencies increase, especially with a large number of parallel requests to one host.
You can check whether the connection through the proxy supports HTTP/2 using cURL with the flag
--http2
and detailed output:
curl -v --http2 -x http://user:pass@proxy_host:8080 https://example.com
# In the output, look for the line:
# * Using HTTP2, server supports multiplexing
# If it is not there — the connection has dropped to HTTP/1.1
For Python, to use HTTP/2 through a proxy, you need the httpx
library with HTTP/2 support enabled:
import httpx
proxies = {
"http://": "http://user:pass@proxy_host:8080",
"https://": "http://user:pass@proxy_host:8080",
}
with httpx.Client(proxies=proxies, http2=True) as client:
response = client.get("https://example.com")
print(response.http_version) # expect "HTTP/2"
print(response.status_code)
An important nuance: HTTP/2 requires TLS with ALPN extension for protocol negotiation, so the proxy must transparently tunnel TLS traffic through CONNECT, rather than terminating TLS itself (unless it is a specialized HTTP/2-compatible proxy). This is why for HTTP/2 tasks, residential proxies and data center proxies behave differently — it is important to clarify with the provider whether end-to-end TLS tunneling without breaking ALPN negotiations is supported.
gRPC through proxy: TLS, ALPN, and special requirements
gRPC is the most demanding protocol in this set. It is strictly tied to HTTP/2 and often uses
mutual TLS (mTLS) for authentication. A regular forward proxy that does not support HTTP/2 CONNECT
tunneling "as is" simply will not pass gRPC traffic — the connection will fail with errors like
UNAVAILABLE: upstream connect error.
To proxy gRPC in Python (using the grpc library),
the proxy is set via environment variables, as there has historically been no built-in support for proxies in channel options:
import os
import grpc
os.environ["grpc_proxy"] = "http://user:pass@proxy_host:8080"
os.environ["https_proxy"] = "http://user:pass@proxy_host:8080"
channel = grpc.secure_channel(
"grpc.example.com:443",
grpc.ssl_channel_credentials()
)
# Example call through the generated stub
# stub = YourServiceStub(channel)
# response = stub.YourMethod(request)
In Node.js, the proxy for gRPC is configured through channel options using grpc-js
in conjunction with an HTTP/2-compatible proxy agent:
const grpc = require('@grpc/grpc-js');
const { HttpsProxyAgent } = require('https-proxy-agent');
process.env.grpc_proxy = 'http://user:pass@proxy_host:8080';
process.env.https_proxy = 'http://user:pass@proxy_host:8080';
const client = new YourServiceClient(
'grpc.example.com:443',
grpc.credentials.createSsl()
);
For stable gRPC operation through a proxy, low latency and a stable TCP connection without frequent drops are critically important — multiplexed gRPC streams are more sensitive to network failures than individual HTTP/1.1 requests. In such scenarios, residential proxies often show better stability compared to cheap data center pools, as the route to the target server goes through a more predictable network infrastructure.
Comparative table of protocols
| Protocol | Connection Type | Proxy Requirements | Recommended Proxy Type |
|---|---|---|---|
| HTTP/1.1 | request-response, keep-alive | minimal, any HTTP proxy | Data center proxies |
| WebSocket | persistent bidirectional channel | support for long connections, no timeouts | Residential proxies |
| HTTP/2 | multiplexed binary streams | end-to-end TLS tunneling, ALPN | Residential or data center proxies |
| gRPC | HTTP/2 + Protobuf, often mTLS | low latency, stability, HTTP/2 CONNECT | Mobile proxies for bypassing strict filters |
How to choose a protocol and proxy for your stack
The choice of protocol is rarely a free decision — it is dictated by the target API. But the choice of proxy type for this protocol is already your responsibility, and here you should rely on the specific scenario:
Data parsing via WebSocket (for example, streaming quotes or real-time data from marketplace websites) requires a stable long connection without timeout drops. Here, residential proxies work better — they are less likely to fall under the anti-fraud algorithms of the target service and maintain connections longer than typical data center pools.
Integration with gRPC microservices via an external proxy (for example, when testing APIs from different geolocations) requires low latency and support for HTTP/2 throughout the path. For this, residential IPs with good routing are suitable, and for time-critical tasks — mobile proxies, if the target service aggressively filters data center ranges.
Massive HTTP/2 requests to REST/GraphQL APIs with multiplexing is the most common scenario where fast and cheap data center proxies suffice, provided the target service does not block data center IP ranges by default.
The general rule: the more "finicky" the protocol is regarding connection stability (WebSocket, gRPC), the more sense it makes to use residential or mobile IPs. The simpler and shorter the request (ordinary HTTP/1.1 or HTTP/2 without long sessions), the easier it is to work with data center proxies — they are faster and provide higher throughput.
Common mistakes and their solutions
Error 1: WebSocket disconnects every 30-60 seconds. The reason is that the proxy or intermediate load balancer closes "inactive" connections. Solution: enable ping/pong frames at the application level with an interval shorter than the proxy timeout (usually 20-25 seconds is sufficient).
Error 2: gRPC fails with UNAVAILABLE through the proxy, although it works directly. The proxy does not support HTTP/2 CONNECT tunneling. Solution: check the provider's documentation for HTTP/2 support, or use a SOCKS5 proxy that tunnels TCP without protocol interpretation.
Error 3: HTTP/2 is silently downgraded to HTTP/1.1. This often happens due to the lack of ALPN support on the proxy. Check the protocol version explicitly in the code (as in the httpx example above) — do not rely on "everything works" if you haven't checked the http_version in the response.
Error 4: High latency when multiplexing gRPC through a proxy. Often caused by a large number of intermediate nodes with the proxy provider. Solution: test latency in advance through a simple RTT request and choose a provider with direct routing, especially for time-sensitive gRPC calls.
Conclusion
WebSocket, HTTP/2, and gRPC require different approaches to proxy configuration precisely because they operate on different transport models: from the simple "release the TCP connection" for WebSocket to strict support for HTTP/2 CONNECT and ALPN for gRPC. Check the protocol version explicitly in the code, test the stability of long connections in advance, and choose SOCKS5 where maximum tunneling transparency is needed.
If your stack includes long-lived WebSocket connections or latency-sensitive gRPC calls, we recommend trying residential proxies — they provide more stable and predictable connections compared to typical data center pools. For simple high-throughput HTTP/2 requests, data center proxies will suffice, while for tasks with aggressive anti-fraud filtering from the target service — mobile proxies.