WebSocket connection (port 21118/21119) is unilaterally closed after exactly ~30 seconds of idle time, even with no reverse proxy involved
hbbs WebSocket connection (port 21118/21119) is unilaterally closed after exactly ~30 seconds of idle time, even with no reverse proxy involved
Environment:
hbbs/hbbrversion: 1.1.16 (latest available at time of writing)- OS: Debian 12, x86_64
- Deployment: systemd services, standard binaries in
/opt/rustdesk-server/ hbbsstarted with:hbbs -r <domain>:21117 -k _hbbrstarted with:hbbr -k _- RustDesk Desktop Client: v1.4.9 (Windows),
allow-websocket = 'Y'set inRustDesk2.toml
Description
When a WebSocket client connects to hbbs on port 21118 (or hbbr on 21119) and completes the handshake but does not send further data immediately, the server closes the connection unilaterally after exactly ~30 seconds, regardless of whether any reverse proxy, firewall, or network component is involved.
This makes the documented allow-websocket=Y client feature effectively unusable against an OSS server whenever the client's registration/heartbeat interval is close to or exceeds this window: the client repeatedly gets disconnected before or right as it would send its next heartbeat, and never reaches a stable "ready" state.
Steps to reproduce (server-local, no proxy/network involved)
Run directly on the machine hosting hbbs:
exec 3<>/dev/tcp/127.0.0.1/21118
printf 'GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: SGVsbG9XZWJTb2NrZXQ=\r\nSec-WebSocket-Version: 13\r\n\r\n' >&3
date
( cat <&3; echo "--- connection closed by server ---"; date ) &
CATPID=$!
sleep 60
if kill -0 $CATPID 2>/dev/null; then
echo "--- still open after 60s ---"
kill $CATPID
fi
exec 3<&- ; exec 3>&-Actual output
Mo 7. Sep 13:31:03 CEST 2026
HTTP/1.1 101 Switching Protocols
connection: Upgrade
upgrade: websocket
sec-websocket-accept: rzNTH42nbDwCim35ggSltPhhUV0=
--- connection closed by server ---
Mo 7. Sep 13:31:33 CEST 2026The server accepts the handshake correctly (101 Switching Protocols), then closes the connection exactly 30.0 seconds later, with zero data sent on either side after the handshake. This is fully reproducible.
Expected behavior
A WebSocket connection with a completed handshake should remain open indefinitely while idle (or at least significantly longer than 30s), consistent with how the raw TCP/UDP rendezvous protocol on ports 21115–21117 behaves.
Real-world symptom (Desktop Client v1.4.9, allow-websocket=Y, connecting via wss:// through a reverse proxy)
[...] INFO [src\rendezvous_mediator.rs:425] start tcp: wss://<domain>/ws/id
[...] DEBUG [...tungstenite-0.26.2\src\handshake\client.rs:101] Client handshake done.
[...] ERROR [...libs\hbb_common\src\websocket.rs:282] WebSocket protocol error: Connection reset without closing handshake
[...] ERROR [src\rendezvous_mediator.rs:177] rendezvous mediator error: WebSocket protocol error: WebSocket protocol error: Connection reset without closing handshake
[...] INFO [src\rendezvous_mediator.rs:482] start rendezvous mediator of <domain>
[...] INFO [src\rendezvous_mediator.rs:425] start tcp: wss://<domain>/ws/idThis reconnect loop repeats indefinitely at ~30-second intervals; the client never reaches a stable "ready" state. On one occasion the reconnect also produced WebSocket protocol error: Reserved bits are non-zero.
What has been ruled out
Extensive testing was done before filing this report:
- Reverse proxy (Apache 2.4.68) — tested with explicit
ProxyTimeout 3600,timeout=3600on theProxyPassdirective, andProxyWebsocketFallbackToProxyHttp Offto force legacymod_proxy_wstunnelhandling. No change. - Two independent client networks — identical failure in both, ruling out client-side firewalls/NAT/DPI.
- Server-side firewall/conntrack —
nf_conntrack_tcp_timeout_establishedat default (432000s), no relevant iptables/UFW rules. - systemd —
hbbs/hbbrhad continuous uptime throughout the failures;Restart=alwayswas never triggered. - No CLI flag or environment variable exists to configure this (confirmed via
hbbs --help/hbbr --helpand the full argument reference).
Additional context
Given allow-websocket is documented as officially supported starting with Client ≥1.4.0, it would be helpful to know whether this 30s idle-close is an intentional design limit (e.g., tied to Pro-server-only usage) or an unintended default in the OSS hbbs/hbbr WebSocket listener implementation. If intentional, documenting it explicitly under the allow-websocket docs would save others the multi-hour debugging process this took.
Happy to provide further logs, tcpdump captures, or run additional tests against hbbs/hbbr if that helps narrow this down.
Source: rustdesk/rustdesk-server