Summary
The documentation states:
Please note, only one WebSocket endpoint is allowed to be active at any time.
This guarantee is enforced only per internal httpServer object. If the existing WebSocket endpoint shares the HTTP server's port, calling admin_startWS with a different port selects the separate n.ws server. The duplicate check does not see the already active endpoint, so both ports remain reachable.
Documentation: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin#admin-startws
Affected revision and environment
- Runtime reproduction: Geth
1.17.5-unstable, commit 81ab8b594ebe0f672450779d4b308f3f33191828
- Platform: macOS arm64, Go
1.26.3
- Initial configuration: HTTP and WebSocket both on port
52387
- Second endpoint: WebSocket on port
52388
- The same server-selection and per-server duplicate check remain on
master at 7e520c43104fd0447acd7372a63ba1daa5c05929 (checked 2026-08-08).
Steps to reproduce
Start Geth with HTTP and WebSocket sharing one port:
PORT_A=52387
./build/bin/geth \
--dev \
--http \
--http.addr 127.0.0.1 \
--http.port "$PORT_A" \
--http.api admin,eth,net,web3 \
--http.corsdomain '*' \
--ws \
--ws.addr 127.0.0.1 \
--ws.port "$PORT_A" \
--ws.api admin,eth,net,web3 \
--ws.origins '*' \
--ipcdisable
In another shell:
#!/usr/bin/env bash
set -euo pipefail
PORT_A="${PORT_A:-52387}"
PORT_B="${PORT_B:-52388}"
RPC_URL="http://127.0.0.1:${PORT_A}"
echo '=== start a second WebSocket endpoint ==='
curl -sS -H 'Content-Type: application/json' --data "{
\"jsonrpc\":\"2.0\",
\"id\":1,
\"method\":\"admin_startWS\",
\"params\":[\"127.0.0.1\",${PORT_B},\"*\",\"eth,net,web3,admin\"]
}" "$RPC_URL"
echo
ws_handshake() {
local port="$1"
curl --http1.1 -sS -i --max-time 2 \
-H 'Connection: Upgrade' \
-H 'Upgrade: websocket' \
-H 'Sec-WebSocket-Version: 13' \
-H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
-H 'Origin: http://127.0.0.1' \
"http://127.0.0.1:${port}/" 2>/dev/null | sed -n '1p' || true
}
echo "=== handshake on original port ${PORT_A} ==="
ws_handshake "$PORT_A"
echo "=== handshake on second port ${PORT_B} ==="
ws_handshake "$PORT_B"
echo '=== same-server duplicate control ==='
curl -sS -H 'Content-Type: application/json' --data "{
\"jsonrpc\":\"2.0\",
\"id\":2,
\"method\":\"admin_startWS\",
\"params\":[\"127.0.0.1\",${PORT_A},\"*\",\"eth,net,web3,admin\"]
}" "$RPC_URL"
echo
Observed behavior
Starting the second endpoint returned success:
{"jsonrpc":"2.0","id":1,"result":true}
Both ports completed the WebSocket upgrade at the same time:
HTTP/1.1 101 Switching Protocols
HTTP/1.1 101 Switching Protocols
Repeating admin_startWS on the original port correctly exercised the local duplicate guard:
{
"error": {
"code": -32000,
"message": "JSON-RPC over WebSocket is already enabled"
}
}
Expected behavior
If only one WebSocket endpoint may be active, the second admin_startWS call should fail or replace the existing endpoint. It should not leave both listener addresses reachable.
Relevant code
Impact
Operators or automation relying on the single-endpoint guarantee can unintentionally leave an older WebSocket RPC endpoint reachable while opening another one. This can increase the exposed RPC surface and cause configuration or shutdown logic to reason about the wrong listener.
Suggested direction
Enforce WebSocket endpoint uniqueness at the Node level before selecting an internal server object, or revise the API contract if multiple endpoints are intentionally supported.
Summary
The documentation states:
This guarantee is enforced only per internal
httpServerobject. If the existing WebSocket endpoint shares the HTTP server's port, callingadmin_startWSwith a different port selects the separaten.wsserver. The duplicate check does not see the already active endpoint, so both ports remain reachable.Documentation: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin#admin-startws
Affected revision and environment
1.17.5-unstable, commit81ab8b594ebe0f672450779d4b308f3f331918281.26.35238752388masterat7e520c43104fd0447acd7372a63ba1daa5c05929(checked 2026-08-08).Steps to reproduce
Start Geth with HTTP and WebSocket sharing one port:
In another shell:
Observed behavior
Starting the second endpoint returned success:
{"jsonrpc":"2.0","id":1,"result":true}Both ports completed the WebSocket upgrade at the same time:
Repeating
admin_startWSon the original port correctly exercised the local duplicate guard:{ "error": { "code": -32000, "message": "JSON-RPC over WebSocket is already enabled" } }Expected behavior
If only one WebSocket endpoint may be active, the second
admin_startWScall should fail or replace the existing endpoint. It should not leave both listener addresses reachable.Relevant code
node/api.go:238-290:admin_startWSselects a server based on the requested port.node/node.go:507-516:wsServerForPortcan return eithern.httporn.ws.node/rpcstack.go:343-367: the “already enabled” check is local to the selectedhttpServer.Impact
Operators or automation relying on the single-endpoint guarantee can unintentionally leave an older WebSocket RPC endpoint reachable while opening another one. This can increase the exposed RPC surface and cause configuration or shutdown logic to reason about the wrong listener.
Suggested direction
Enforce WebSocket endpoint uniqueness at the
Nodelevel before selecting an internal server object, or revise the API contract if multiple endpoints are intentionally supported.