You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/1.guide/7.proxy.md
+62-15Lines changed: 62 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,6 +30,34 @@ Every incoming peer opens a matching upstream connection. Text and binary messag
30
30
> [!TIP]
31
31
> Messages sent by the client before the upstream connection is ready are buffered and flushed as soon as the upstream is open.
32
32
33
+
> [!CAUTION]
34
+
> **The default proxy is an open relay.** It accepts every incoming connection and forwards it to the configured upstream without any authorization check. Always combine it with an [`upgrade` hook](#authentication) when the upstream is not itself publicly accessible — otherwise anyone who can reach the proxy can reach the upstream.
35
+
36
+
## Authentication
37
+
38
+
`createWebSocketProxy()` returns a plain hooks object, so you can spread it and override individual hooks. Authenticate the upgrade request before proxying by wrapping the proxy's `upgrade` hook:
// Delegate to the proxy's own `upgrade` so subprotocol echoing still works.
53
+
returnproxyHooks.upgrade?.(req);
54
+
},
55
+
};
56
+
```
57
+
58
+
> [!NOTE]
59
+
> The WHATWG `WebSocket` constructor cannot forward cookies, `Authorization`, or `Origin` to the upstream, so upstream identity checks relying on those headers will silently fail. Authenticate at the proxy, or pass a custom `WebSocket` client and use the [`headers` option](#forwarding-headers) to propagate identity.
60
+
33
61
## Dynamic target
34
62
35
63
Pass a function to resolve the upstream URL from the incoming [`Peer`](/guide/peer) — useful for routing based on request URL, headers, or authenticated context.
> **SSRF risk.** A dynamic `target` resolver is a trust boundary. Never interpolate untrusted input (query strings, headers, path segments a client controls) directly into the returned URL — a naive resolver turns the proxy into an SSRF primitive that can dial `ws://127.0.0.1`, `ws://169.254.169.254`, or any reachable internal service. Always resolve against a hard-coded allowlist of hosts you control.
80
+
50
81
## Subprotocol negotiation
51
82
52
83
By default, the proxy forwards the client's `sec-websocket-protocol` header to the upstream and echoes the first requested subprotocol back in the upgrade response so the client handshake succeeds. Disable this if you want to negotiate subprotocols yourself:
`createWebSocketProxy()` returns a plain hooks object, so you can spread it and override individual hooks — for example, to authenticate the upgrade request before proxying:
111
+
The proxy does not enforce any scheme allowlist — whatever the configured `WebSocket` constructor accepts is accepted. For example, the [`ws`](https://github.com/websockets/ws) package supports Unix domain sockets via its `ws+unix:` scheme:
Passing a `headers` option attaches extra headers to the upstream handshake. This is the usual way to forward identity (`cookie`, `authorization`, `origin`) or inject a shared secret to the upstream.
> The WHATWG global `WebSocket` constructor does **not** accept custom headers. `headers` is only honored when a `WebSocket` constructor that takes a third options argument is passed via the [`WebSocket` option](#custom-websocket-constructor) — e.g. [`ws`](https://github.com/websockets/ws) or [`undici`](https://undici.nodejs.org). With the global constructor the option is silently ignored.
143
+
98
144
## API
99
145
100
146
### `createWebSocketProxy(target)`
101
147
102
148
Accepts either a target URL (`string` or `URL`), a resolver function, or an options object:
103
149
104
-
-**`target`** — `string | URL | (peer: Peer) => string | URL`. The upstream WebSocket URL, or a function that resolves it per peer.
105
-
-**`forwardProtocol`** — `boolean` (default `true`). When enabled, the client's `sec-websocket-protocol` header is forwarded to the upstream and echoed back in the upgrade response.
106
-
-**`maxBufferSize`** — `number` (default `1048576`, i.e. 1 MiB). Maximum number of bytes buffered per peer while the upstream is still connecting. When exceeded, the peer is closed with code `1009` (Message Too Big). Set to `0` to disable.
150
+
-**`target`** — `string | URL | (peer: Peer) => string | URL`. The upstream WebSocket URL, or a function that resolves it per peer. The proxy does not enforce a scheme allowlist; any URL the configured `WebSocket` constructor accepts (including `ws+unix:` with `ws`) works. See the [SSRF warning](#dynamic-target) before using a dynamic resolver.
151
+
-**`forwardProtocol`** — `boolean` (default `true`). When enabled, the client's `sec-websocket-protocol` header is forwarded to the upstream and echoed back in the upgrade response. Values that are not valid RFC 7230 tokens are dropped.
152
+
-**`headers`** — `HeadersInit | (peer: Peer) => HeadersInit`. Extra headers to send on the upstream handshake. Only honored when a custom `WebSocket` constructor that accepts a third options argument is supplied — the WHATWG global ignores it.
153
+
-**`maxBufferSize`** — `number` (default `1048576`, i.e. 1 MiB). Maximum number of bytes buffered per peer while the upstream is still connecting. String frames are accounted at their UTF-8 worst case (3 bytes per UTF-16 code unit) to avoid undercounting multi-byte content. When exceeded, the peer is closed with code `1009` (Message Too Big). Set to `0` to disable.
107
154
-**`connectTimeout`** — `number` (default `10000`). Milliseconds to wait for the upstream WebSocket handshake to complete. If exceeded, the peer is closed with code `1011`. Set to `0` to disable.
108
155
-**`WebSocket`** — `typeof WebSocket` (default `globalThis.WebSocket`). Custom `WebSocket` constructor used to dial the upstream. Falls back to the global when omitted; throws at setup time if neither is available.
0 commit comments