Allow the UI to be served on custom hosts (reverse proxy / Tailscale)#1333
Allow the UI to be served on custom hosts (reverse proxy / Tailscale)#1333mihow wants to merge 4 commits into
Conversation
…ED_HOSTS Newer Vite rejects `vite preview` requests whose Host header is not localhost. Add an env-driven `preview.allowedHosts` so proxied deployments can declare accepted hostnames. Unset preserves Vite's localhost default; local dev is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for antenna-ssec ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for antenna-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Need the big picture first? Review this PR in Change Stack to see what changed before going file by file. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis pull request adds support for custom hostnames on the Vite UI server via a new ChangesUI Allowed Hosts Feature
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds an opt-in configuration to the UI’s vite preview server so the built frontend can be served behind a reverse proxy on non-localhost hostnames, via a PREVIEW_ALLOWED_HOSTS environment variable.
Changes:
- Adds
preview.allowedHoststoui/vite.config.ts, sourced fromPREVIEW_ALLOWED_HOSTS. - Keeps default Vite behavior when
PREVIEW_ALLOWED_HOSTSis unset (localhost-only).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The initial change only set `preview.allowedHosts`, which covers `vite preview` (the production-style `ui` container). The local dev server (`vite` / `yarn start`, used by the `ui-dev` service and by contributors) is a separate Vite server with its own `server.allowedHosts`, so it still returned "This host is not allowed" when reached on a non-localhost hostname such as a Tailscale name. - Add `server.allowedHosts` sourced from a new `DEV_ALLOWED_HOSTS` env var, mirroring `PREVIEW_ALLOWED_HOSTS`. - Extract a shared `parseAllowedHosts` helper (comma-split, trim, drop empty entries from trailing commas; undefined when unset to keep Vite's localhost-only default). - Document both vars in ui/README.md and as commented examples on the ui / ui-dev services in docker-compose.yml. Verified on the dev server: localhost -> 200, allowed host -> 200 (serves the app), unlisted host -> 403. Co-Authored-By: Claude <noreply@anthropic.com>
Collapse the two host-allowlist variables into a single `UI_ALLOWED_HOSTS`. A container runs either the dev server (`yarn start`) or the preview server (`yarn preview`), never both, so one variable can feed both `server.allowedHosts` and `preview.allowedHosts` — operators set it once without needing to know which server their container runs. Mirrors the existing `DJANGO_ALLOWED_HOSTS` naming. `DJANGO_ALLOWED_HOSTS` itself isn't reused: it belongs to the Django backend (a different process and container), isn't present in the UI service env, and frequently contains `*`, which Vite would treat as a literal hostname rather than "allow all". Co-Authored-By: Claude <noreply@anthropic.com>
…mpose Move the host-allowlist example out of docker-compose.yml (operators shouldn't be editing the committed compose file) into a committed ui/.env.example. Contributors copy it to ui/.env.local (gitignored) and set their own value. Co-Authored-By: Claude <noreply@anthropic.com>

Summary
A recent Vite upgrade (pulled in with the dependency update) tightened Vite's servers so they reject any request whose
Hostheader isn'tlocalhost. That's a reasonable default, but it breaks any setup that serves the UI on a real hostname — behind a reverse proxy, or simply reached over a Tailscale name. Instead of the app, the browser gets a plain-text page saying "This host is not allowed."This change adds an opt-in
UI_ALLOWED_HOSTSenvironment variable to declare the hostnames the UI should accept. Local development is unchanged — when the variable is unset, Vite keeps its localhost-only default, so contributors running the app locally see no difference.One variable covers both of Vite's servers, which are configured separately:
vite/yarn start, used by theui-devcompose service and by contributors), viaserver.allowedHosts; andvite preview, used by the production-styleuicontainer), viapreview.allowedHosts.A given container only runs one of those, so a single variable is enough and operators don't need to know which server their box runs.
List of Changes
UI_ALLOWED_HOSTSfeeds bothserver.allowedHostsandpreview.allowedHostsinui/vite.config.ts..example.commatches all subdomains. Empty entries (e.g. a trailing comma) are ignored, and leaving it unset preserves Vite's localhost-only default.parseAllowedHostshelper: split on commas, trim, drop empties, returnundefinedwhen nothing is configured.ui/README.mdConfiguration section, plus a committedui/.env.example(copy to the gitignoredui/.env.local).Detailed Description
Newer Vite versions added a Host-header allowlist to both the dev and preview servers as an SSRF/DNS-rebinding mitigation, defaulting to localhost only. Deployments (or local setups) that reach a Vite server on a custom domain therefore need to declare the allowed hostnames. Rather than hardcode any specific domain, the config reads the list from the environment so each deployment supplies its own.
The existing
DJANGO_ALLOWED_HOSTSis intentionally not reused: it belongs to the Django backend (a separate process and container), is not present in the UI service's environment, and frequently contains*, which Vite would treat as a literal hostname rather than "allow all".UI_ALLOWED_HOSTSmirrors its naming while keeping the frontend and backend allowlists independent.How to Test the Changes
Dev server:
UI_ALLOWED_HOSTS=my-host.example.com yarn start --host 0.0.0.0 --port 4000(or set it on theui-devcompose service).curl -H 'Host: my-host.example.com' http://localhost:4000/→ returns the app (HTTP 200).curl -H 'Host: other.example.com' http://localhost:4000/→ still HTTP 403 "host not allowed".Preview server: same as above with
yarn buildthenUI_ALLOWED_HOSTS=… vite preview --host 0.0.0.0 --port 4000.Verified against the running
ui-devdev server: the configured Tailscale host returned HTTP 200 and served the app,localhostwas unaffected, and an unlisted host still returned HTTP 403.Deployment Notes
Set
UI_ALLOWED_HOSTSin the UI service's environment for any setup reached on a non-localhost host (comma-separated hostnames, or a leading-dot wildcard to cover subdomains). The same variable applies whether the container runsvite previewor theyarn startdev server. No effect if left unset.Summary by CodeRabbit
New Features
Documentation