Summary
LAN or remote viewers can load the serve-sim UI through a host such as minipro24.lan, but the preview can switch back to loopback helper URLs after the UI subscribes to /api/events through /exec-ws.
Environment
- Observed with
serve-sim@0.1.41
- Upstream
main contains the same source behavior before the proposed fix
- macOS on Apple Silicon
- Xcode 26.5
- Browser running from another LAN machine or a remote browser preview
Steps to reproduce
-
Start serve-sim on all interfaces:
npx --yes serve-sim@0.1.41 "iPhone 17" --port 3200 --host 0.0.0.0
-
Open the UI from another machine:
http://minipro24.lan:3200
-
Observe that the initial HTML config can contain the LAN host.
-
Wait for the UI to subscribe to /api/events through /exec-ws.
Expected behavior
The /api/events preview config keeps helper URLs reachable by the remote viewer, for example:
http://minipro24.lan:3100/stream.mjpeg
ws://minipro24.lan:3100/ws
Actual behavior
The browser starts retrying loopback helper URLs, for example:
http://127.0.0.1:3100/stream.mjpeg?raw=1
ws://127.0.0.1:3100/ws
A direct request to http://minipro24.lan:3200/api/events returns the expected LAN host, so the issue appears specific to the /exec-ws SSE proxy path.
Root cause
packages/serve-sim/src/exec-ws.ts proxies allowed SSE subscriptions by making a loopback httpRequest to 127.0.0.1:<serverPort>, but it does not forward the viewer's original Host header.
That means /api/events sees a loopback request, so rewriteStateForRequestHost treats it as local and leaves the helper URLs on 127.0.0.1.
Related work
Proposed fix
I have a small PR ready that forwards the WebSocket upgrade Host header into allowed SSE loopback requests and adds regression coverage for both LAN and loopback preview config URLs.
Summary
LAN or remote viewers can load the serve-sim UI through a host such as
minipro24.lan, but the preview can switch back to loopback helper URLs after the UI subscribes to/api/eventsthrough/exec-ws.Environment
serve-sim@0.1.41maincontains the same source behavior before the proposed fixSteps to reproduce
Start serve-sim on all interfaces:
npx --yes serve-sim@0.1.41 "iPhone 17" --port 3200 --host 0.0.0.0Open the UI from another machine:
Observe that the initial HTML config can contain the LAN host.
Wait for the UI to subscribe to
/api/eventsthrough/exec-ws.Expected behavior
The
/api/eventspreview config keeps helper URLs reachable by the remote viewer, for example:Actual behavior
The browser starts retrying loopback helper URLs, for example:
A direct request to
http://minipro24.lan:3200/api/eventsreturns the expected LAN host, so the issue appears specific to the/exec-wsSSE proxy path.Root cause
packages/serve-sim/src/exec-ws.tsproxies allowed SSE subscriptions by making a loopbackhttpRequestto127.0.0.1:<serverPort>, but it does not forward the viewer's originalHostheader.That means
/api/eventssees a loopback request, sorewriteStateForRequestHosttreats it as local and leaves the helper URLs on127.0.0.1.Related work
/exec-wscontrol channel and SSE side-channels./exec-wsand appears to have a different root cause.Proposed fix
I have a small PR ready that forwards the WebSocket upgrade
Hostheader into allowed SSE loopback requests and adds regression coverage for both LAN and loopback preview config URLs.