[Fix] HMR WebSockets rejected in sandbox previews - #1217
Open
mrubens wants to merge 1 commit into
Open
Conversation
Vite refuses requests and HMR WebSocket upgrades whose Host header is not in `server.allowedHosts`, and Astro, Nuxt and SvelteKit inherit that check. Two different hosts reach a sandbox dev server: the sandbox's own host on plain HTTP, because the preview proxy changes origin, and the public preview host on upgrades, because the auth-proxy rewrites Host there. Only the first was ever allowlisted — by each repo hardcoding a preview domain in its own config — so previews rendered while HMR died with a 400 and live reload silently stopped working. Set `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` in the worker-managed shell env file to both hosts, as exact hostnames for the current task rather than a wildcard suffix. Vite reads the variable when it resolves config and appends it to allowedHosts, so HMR works without per-repo configuration. An explicit deployment-provided value still wins.
Contributor
| ].filter((hostname) => !VITE_UNSAFE_ALLOWED_HOST_CHARS.test(hostname)); | ||
|
|
||
| if (allowedHosts.length > 0) { | ||
| envVars.__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS = allowedHosts.join(','); |
Contributor
There was a problem hiding this comment.
Vite treats __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS as one host, not a comma-separated list: its resolver appends the entire environment value as a single allowedHosts entry. With the normal preview and sandbox hostnames, this produces task-123-web.preview...,sandbox-web... as one value, which matches neither Host header, so the HMR upgrade remains rejected. The new tests only split the value themselves and therefore do not exercise Vite's parsing behavior.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
HMR is broken in sandbox previews for any Vite-based app (Astro, Nuxt, SvelteKit included). The page renders and every HTTP request returns 200, but the HMR WebSocket never connects, so live reload silently does nothing.
Vite gates both requests and HMR upgrades on
server.allowedHosts. Two different hosts reach a sandbox dev server:changeOrigin: true.Hoston the upgrade path (startMultiplexAuthProxy).Only the first was ever allowlisted, and only because individual repos hardcoded a preview domain in their own config. The upgrade is therefore rejected with a 400 — which is why previews render but HMR does not work.
Fix
Set
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTSin the worker-managed shell env file (~/.roomote/env.sh, sourced viaBASH_ENVand.bashrc) to both hosts. Vite reads the variable when it resolves config and appends it toallowedHosts, so HMR works with no per-repo configuration.The value is pinned to the current task's exact hostnames rather than a wildcard suffix, deduplicated across named ports, and reuses the preview URLs
injectEnvVarsalready builds:The code-server port is excluded (it is not a user dev server), the nested-preview subdomain suffix is carried through, and an explicit deployment-provided value still wins — matching how
PREVIEW_DOMAINSbehaves directly below it.Trade-off worth knowing
Exact hostnames mean a port registered after the env file is written is not covered until the file is refreshed and the dev server restarts (Vite reads the variable at startup). Nested previews are covered only for the suffix the task was spawned with. Switching to
.${previewDomain}is a one-line change if either becomes a problem.Testing
pnpm vitest run src/commands/__tests__/utils.test.tsinapps/worker— 38 pass, 7 new: exact preview hostnames, sandbox machine host, dedup across ports, nested subdomain suffix, code-server exclusion, deployment override, and no-base-URL.pnpm check-types,pnpm lint,pnpm format:checkclean; pre-push checks pass.Follow-up in another repo
Once this ships, repos no longer need their own preview-domain allowlists. The website's
astro.config.mjshas a stale one ready to delete — but it must land after this rolls out to the sandbox image, since one of its entries is currently what makes HTTP previews work.