8000 fix(url-util): infer backendUrl from location when behind a path-prefixing proxy by luissilva-mollie · Pull Request #516 · google/adk-web · GitHub
[go: up one dir, main page]

Skip to content

fix(url-util): infer backendUrl from location when behind a path-prefixing proxy - #516

Closed
luissilva-mollie wants to merge 1 commit into
google:mainfrom
luissilva-mollie:fix/backend-url-behind-path-prefix
Closed

luissilva-mollie wants to merge 1 commit into
google:mainfrom
luissilva-mollie:fix/backend-url-behind-path-prefix

Conversation

@luissilva-mollie
Copy link
Copy Markdown

Fixes #515.

Problem

URLUtil.getApiServerBaseUrl() defaults to '' (root-relative) when runtimeConfig.backendUrl isn't configured. That's fine when adk-web is served at the server root, but breaks any deployment behind a path-prefixing reverse proxy that strips its prefix before forwarding to the backend: the UI loads correctly at e.g. /agents/my-agent/dev-ui/, but its API calls go out root-relative (GET /list-apps) and the proxy has no route for that.

runtime-config.json can be customized via set-backend.js, but that only runs at build time against the source tree. Backends that embed an already-built adk-web bundle as a packaged dependency (e.g. adk-java's AdkWebServer, serving classpath:/browser/** out of the google-adk-dev jar) have no build step to run it against, and the file is sealed inside a jar at runtime — so there was no way to get a correct backendUrl in that scenario without patching the frontend.

getBaseUrlWithoutPath() (used as the OAuth redirectUri) had the same class of bug: it hardcodes origin + '/dev-ui/', discarding any prefix.

Fix

When backendUrl isn't explicitly configured, derive a same-origin default from window.location that preserves whatever path prefix the app is itself mounted under (by locating /dev-ui in location.pathname and taking everything before it as the mount root), instead of always assuming the API lives at the server root. Applied the same fix to getBaseUrlWithoutPath().

An explicit runtimeConfig.backendUrl (e.g. set via set-backend.js for local dev) still takes precedence, so local dev workflows are unaffected.

Refactored the three URLUtil methods to accept an optional Location-like parameter (defaulting to window.location) purely for testability — window.location isn't reconfigurable in a real browser, so this was needed to unit test the new branching without reaching for a more invasive mocking setup.

Testing

  • Added src/utils/url-util.spec.ts covering both the unprefixed and reverse-proxy-prefixed cases for all three methods, plus the "explicit config wins" case.
  • ng test (full suite): 650 passed, no regressions.
  • ng build: succeeds.

…ixing proxy

Backends that embed the prebuilt adk-web bundle (e.g. adk-java's
AdkWebServer) serve runtime-config.json straight out of their own
packaged artifact and have no build step to run set-backend.js against,
so backendUrl is always "". URLUtil.getApiServerBaseUrl() then falls back
to root-relative API calls (GET /list-apps), which breaks behind any
reverse proxy that strips a path prefix before forwarding to the backend
(e.g. WAP-style routing: /agents/my-agent/* -> container, prefix
stripped) — the browser calls the unprefixed path and the proxy 404s.

Derive a same-origin default from window.location instead of defaulting
to root-relative, preserving whatever prefix this app was itself loaded
under. An explicit runtimeConfig.backendUrl still takes precedence, so
local dev via set-backend.js is unaffected. Also fixes
getBaseUrlWithoutPath(), which hardcoded "/dev-ui/" off the origin and
dropped any prefix the same way.
@wyf7107
wyf7107 commented Sep 28, 2026
Copy link
Copy Markdown
Collaborator

Thanks for the detailed write-up and the tests! Since this PR was opened, the dev UI runtime config has moved from a static runtime-config.json in the bundle to being served by the backend (the UI fetches it relative to where it is mounted), which lets the server that knows about the reverse-proxy prefix provide the right backendUrl. That is the direction we want for prefixed deployments, rather than inferring the prefix from the page location on the client, so I am closing this one. For backends that embed the built bundle (such as adk-java's AdkWebServer), the fix would be for that server to serve the runtime config as well; feel free to open an issue if you hit problems with that.

@wyf7107 wyf7107 closed this Sep 28, 2026
@luissilva-mollie
Copy link
Copy Markdown
Author

Thanks for the explanation, that makes sense, and we've adopted it on the Python side: google-adk 2.11.0 serves /dev-ui/assets/config/runtime-config.json from the backend using --url_prefix, and that fixed our path-prefixed deployment.

A clarification for adk-java: as far as I can tell this isn't supported there yet. In google-adk-dev 1.10.1 (latest on Maven Central), AdkWebServer has no runtime-config handling. The jar still bundles the static browser/assets/config/runtime-config.json with {"backendUrl": ""}, and it's served by the plain /dev-ui/** resource handler. Since that file is sealed in the jar, users behind a prefixing reverse proxy have to intercept the request themselves, as we do with a servlet filter.

Is a backend-served runtime config planned for adk-java? If so, is there an issue we can follow? If not, we're happy to open one.

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.

backendUrl defaults to root-relative, breaking deployments behind a path-prefixing reverse proxy

2 participants

0