feat: support deployment behind a reverse proxy under a sub-path#9306
Open
Pfannkuchensack wants to merge 4 commits into
Open
feat: support deployment behind a reverse proxy under a sub-path#9306Pfannkuchensack wants to merge 4 commits into
Pfannkuchensack wants to merge 4 commits into
Conversation
The web frontend derived all URLs from window.location.origin and hardcoded paths (/api, /ws/socket.io, /locales, /openapi.json), so running Invoke behind a reverse proxy under a sub-path (e.g. https://host/invoke/) broke because the prefix was lost. Frontend: add a shared helper (common/util/baseUrl.ts) that auto-detects the deployment prefix from the entry bundle URL (import.meta.url), relying on Vite's existing `base: './'`. getBaseUrl(), the socket.io path, i18n loadPath, the openapi fetch and the React Router basename now all use it. At the domain root the prefix is empty, so behavior is byte-identical to before. Covered by unit tests. Backend: add an opt-in `base_url` setting (plus `forwarded_allow_ips`). When set, the app is wrapped in SubPathASGIMiddleware which strips the prefix from the request path and advertises root_path, handling both proxy styles (preserve and strip). uvicorn's root_path is intentionally not used, as it prepends the prefix and breaks the preserve case. Default (unset) leaves existing installs unchanged. Tested end-to-end with Docker + Caddy for preserve, strip, and zero-config strip.
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.
Summary
The web frontend derived all URLs from
window.location.originand used hardcoded paths (/api,/ws/socket.io,/locales,/openapi.json), so running Invoke behind a reverse proxy under a sub-path (e.g.https://host/invoke/) broke because the prefix was lost.Frontend: adds a shared helper (
common/util/baseUrl.ts) that auto-detects the deployment prefix from the entry bundle URL (import.meta.url), relying on Vite's existingbase: './'.getBaseUrl(), the socket.io path, the i18nloadPath, the openapi fetch and the React Routerbasenamenow all use it. At the domain root the prefix is empty, so behavior is byte-identical to before. Covered by unit tests.Backend: adds an opt-in
base_urlsetting (plusforwarded_allow_ips). When set, the app is wrapped inSubPathASGIMiddleware, which strips the prefix from the request path and advertisesroot_path, handling both proxy styles (preserve and strip). uvicorn'sroot_pathis intentionally not used, because it prepends the prefix to the request path and breaks the preserve case (the prefix would appear twice). Default (unset) leaves existing installs completely unchanged.Two deployment scenarios are supported:
base_urlis optional but recommended so/docs+ openapi URLs are correct.base_urlso the middleware can strip the prefix for routing.QA Instructions
Tested end-to-end with Docker + a Caddy reverse proxy in three configurations; every endpoint was checked for the correct content type and body (not just status code):
/docsbase_url=/invoke/invoke/openapi.json)base_url=/invokebase_urlunset)The frontend prefix-detection logic is also covered by unit tests (
baseUrl.test.ts).To reproduce locally (production build required — the dev server has no
/assets/segment, so auto-detection is a no-op there):Strip proxy (Caddy):
Leave
base_urlunset (or setbase_url: /invoketo also fix/docs).Preserve proxy (nginx):
Set
base_url: /invokeandforwarded_allow_ips: <proxy-ip>ininvokeai.yaml.Then browse
http://localhost:8080/invoke/and confirm: UI loads, generations stream progress (websocket), language switching loads locales, and a reload restores client state. Also verify a regression check at the domain root (http://localhost:9090/) still works withbase_urlunset.Merge Plan
Normal merge. No DB schema or redux slice changes. The config schema gains two opt-in fields (
base_url,forwarded_allow_ips); both default to a no-op, so no migration is needed.Checklist
What's Newcopy (if doing a release after this PR)