Routing and rewrites
Proxy a WebSocket
Pass an HTTP upgrade through the proxy and verify that a long-lived bidirectional connection survives.
10 minute lesson
WebSockets start with an HTTP upgrade, then continue as a long-lived connection. The client sends a normal GET with Upgrade: websocket and Connection: Upgrade headers. The server answers 101 Switching Protocols, and from that moment the connection stops being HTTP — it’s a bidirectional byte stream that stays open for minutes or hours.
That’s a different beast from the request/response traffic we’ve proxied so far, and it’s worth proving your proxy handles it. Caddy’s HTTP reverse proxy supports the upgrade path without any special configuration.
Proxy the socket path
Use a WebSocket-capable lab backend — the ws package gives you one in a few lines — then proxy its path:
handle /socket {
reverse_proxy 127.0.0.1:4001
}
Nothing WebSocket-specific in the config. Caddy detects the upgrade headers and switches to streaming mode for that connection on its own.
Verify the upgrade
You can watch the handshake succeed with plain curl by sending the upgrade headers yourself:
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
http://127.0.0.1:8080/socket
# HTTP/1.1 101 Switching Protocols
The 101 proves the upgrade traveled through the proxy to the backend and back. For real message exchange, connect with a WebSocket client through port 8080 and send a few messages both ways.
Restart the backend
Here’s the part that makes long-lived connections operationally different. With a client connected, restart the backend and observe how existing and new connections fail differently.
The existing connection dies immediately — the proxy can’t preserve a TCP stream to a process that’s gone. New connections fail until the backend is back, then succeed. Your open client doesn’t automatically reconnect; that’s the application’s job.
This is why WebSocket-heavy systems obsess over reconnect logic and connection draining. Every proxy reload, backend deploy, or health-based removal severs live connections that HTTP clients would never notice. Plan connection draining and reconnect behavior before deploying a proxy change around long-lived clients — the config change is trivial, the client experience is not.
Lesson completed