Skip to content

fix(websockets): don't reject same-origin WS upgrades behind port-stripping proxies#269

Open
rmounce wants to merge 2 commits into
selkies-project:comprehensive-fixesfrom
rmounce:rmounce/cf-ws-origin-port
Open

fix(websockets): don't reject same-origin WS upgrades behind port-stripping proxies#269
rmounce wants to merge 2 commits into
selkies-project:comprehensive-fixesfrom
rmounce:rmounce/cf-ws-origin-port

Conversation

@rmounce

@rmounce rmounce commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

_is_ws_origin_allowed compares the Origin header against the forwarded Host. Behind a reverse proxy configured with proxy_set_header Host $host (nginx $host strips the port — this is what linuxserver.io's bundled config does), the browser's Origin carries an explicit port (e.g. https://example.com:3001) while the forwarded Host has none, so the exact string comparison fails and every same-origin websocket upgrade is rejected — with no hint client-side, since the 403 happens before the upgrade.

Fix: when the forwarded Host carries no port, fall back to comparing hostnames only. Exact host:port matching is preserved whenever the proxy forwards a port. Checked against IPv6 literals, Origin: null, and genuine cross-site origins (still rejected).

Found and tested while second-eye testing #254 on an lsio-based image (their nginx proxies the websocket with Host $host on port 3001).

🤖 Generated with Claude Code

ehfd and others added 2 commits July 7, 2026 13:17
…ipping proxies

The Origin guard's same-origin fallback compared urlsplit(origin).netloc
against the Host header verbatim. Reverse proxies routinely forward Host
without the port -- the linuxserver baseimage that deploys Selkies does
('proxy_set_header Host $host'; nginx's $host strips it) -- while a
browser Origin carries any non-default port. A same-origin client
reaching the container via an explicit port (e.g. the '-p 3001:3001'
mapping in linuxserver's docs, or any remapped port) was therefore 403'd
in a reconnect loop; only default-port (443/80) deployments worked.

When the forwarded Host carries no port to compare, fall back to
comparing hostnames. A Host that does include a port still requires an
exact netloc match, and cross-site Origins remain rejected either way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies the WebSocket origin validation in src/selkies/stream_server.py to handle scenarios where reverse proxies forward the Host header without a port, while the browser's Origin header contains a non-default port. It now falls back to comparing hostnames when the forwarded Host does not specify a port. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@ehfd ehfd force-pushed the comprehensive-fixes branch 2 times, most recently from 155eb0b to de77558 Compare July 7, 2026 10:25
@ehfd

ehfd commented Jul 7, 2026

Copy link
Copy Markdown
Member

Fixes incorporated in de77558, linuxserver/pixelflux@b32a178, linuxserver/pcmflux@0cbadcb.

@ehfd ehfd force-pushed the comprehensive-fixes branch 18 times, most recently from 3bab531 to 4de4c8a Compare July 9, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants