Skip to content

feat(app): support serving the explorer from a configurable base path#617

Open
chai-guy-7 wants to merge 2 commits into
matter-labs:mainfrom
chai-guy-7:feat/app-base-path-support
Open

feat(app): support serving the explorer from a configurable base path#617
chai-guy-7 wants to merge 2 commits into
matter-labs:mainfrom
chai-guy-7:feat/app-base-path-support

Conversation

@chai-guy-7

@chai-guy-7 chai-guy-7 commented Jun 3, 2026

Copy link
Copy Markdown

What

Adds support for serving the explorer frontend from a configurable URL base path (e.g. https://example.com/explorer/) instead of only at the domain root. Motivated by a partner reverse-proxying x.xx/explorer to their hosted explorer instance, which currently breaks because all assets, the runtime config.js, and several JS redirects assume the app lives at /.

How

Two configuration knobs, both backward compatible (defaults reproduce current behavior exactly):

  • Build time: VITE_BASE_PATH=/explorer/ npm run build (also works for npm run dev). Sets the Vite base and keeps the injected <base> tag in sync.
  • Container runtime (no rebuild): the Docker image is now built with a relative Vite base, and docker run -e BASE_PATH=/explorer ... configures everything at container start. This fits the existing pattern where index.html is templated with gomplate on startup, so the stock published image can serve any subpath.

Mechanism:

  • index.html gets a <base href> tag templated from BASE_PATH (default /). Built asset URLs are emitted relative in Docker builds and resolve against it. config.js and the favicon defaults are now base-relative.
  • vue-router uses createWebHistory() with no argument, which reads the <base> tag, covering both build-time and runtime configuration.
  • New src/utils/basePath.ts helpers (getBasePath, basePathPrefix, publicAsset, appUrl, appRootUrl, currentAppPath) with unit tests. All root-absolute /images/... references, the 401 login redirect, the wallet-switch reload, the Prividium OAuth redirect_uri / post_logout_redirect_uri, and MetaMask blockExplorerUrls resolve through them. For root deployments every helper returns the exact same strings as before (including https://host without a trailing slash where external allowlists may do exact matching).
  • New nginx.conf.subpath.template used only when BASE_PATH is set: serves the app under the prefix (with a 301 from the bare prefix, relative redirects for proxy friendliness, and the same security headers). When BASE_PATH is unset, the existing template is used unchanged and the rendered nginx config is byte-identical to today.
  • Dockerfile: VITE_BASE_PATH build arg, and the CMD normalizes BASE_PATH and picks the right template at startup. Idempotent across container restarts (index.html is always regenerated from the .tpl).

release.yml is intentionally untouched: the published image stays a single artifact, configurable per deployment via BASE_PATH.

Verification

  • Full unit test suite passes (777 tests), plus new tests for the base-path helpers. Lint and vue-tsc clean.
  • Built and served the app three ways against a real nginx (1.29) using the rendered configs from both templates:
    • root, no BASE_PATH: rendered nginx config identical to current, app loads, deep links work, browser console clean.
    • BASE_PATH=/explorer direct: /explorer 301s to /explorer/, config.js, JS/CSS chunks, images, and deep links (/explorer/tx/0x...) all 200, client-side navigation keeps the prefix, zero failed requests in a headless-browser run.
    • behind a prefix-preserving reverse proxy (simulating the partner setup): same results.
  • Dockerfile CMD normalization exercised for all BASE_PATH input forms (``, /, `/explorer`, `/explorer/`, `explorer`, `explorer/`).
  • Code review and security review performed; review findings (MetaMask/OAuth trailing-slash parity at root, bare-prefix redirect edge case, duplicated slash-trimming) were addressed in this PR. The security review found no newly introduced vulnerabilities: OAuth redirect URIs remain pinned to window.location.origin, and the nginx prefix rewrite is applied after nginx's standard URI normalization, so no traversal is possible.

Note: container-level smoke testing of the actual Docker image (docker build + docker run with and without BASE_PATH) was not run in this environment (no Docker available); the rendered nginx configs and the startup shell logic were tested directly against a real nginx binary instead. Worth a quick staging check on the built image.

Deployment notes

  • For the partner case: deploy the stock image with BASE_PATH=/explorer and have their proxy forward /explorer/* to the container without stripping the prefix.
  • If a proxy strips the prefix instead, leave BASE_PATH unset (root mode), but the app will emit root-relative URLs, so prefix-preserving proxying is the supported setup.
  • BASE_PATH must be a plain path prefix (letters, digits, -, _, /), as it is interpolated into an nginx location/rewrite.

Allow the frontend to be served under a URL subpath (e.g. /explorer)
instead of only at the domain root, for deployments behind
prefix-preserving reverse proxies.

- vite.config.ts: base from VITE_BASE_PATH (build time); Docker builds
  use a relative base so one image works at any subpath
- index.html: <base href> tag templated from BASE_PATH (gomplate at
  container start or the html-transform plugin locally); config.js and
  favicon defaults are now base-relative
- new src/utils/basePath.ts: getBasePath/basePathPrefix/publicAsset/
  appUrl/appRootUrl/currentAppPath helpers reading the live <base> tag
- router uses createWebHistory() so vue-router picks up the <base> tag
- absolute /images/... references and window.location redirects
  (401 login redirect, wallet switch reload, OAuth redirect_uri and
  post_logout_redirect_uri, MetaMask blockExplorerUrls) now resolve
  through the helpers; root-deployment output is unchanged
- nginx.conf.subpath.template: serves under ${BASE_PATH} prefix when
  the BASE_PATH env var is set; the existing template and rendered
  config remain untouched when it is unset
- Dockerfile: VITE_BASE_PATH build arg; CMD normalizes BASE_PATH and
  selects the nginx template at container start

Verified at root and under /explorer (direct and behind a
prefix-preserving proxy) against a real nginx with the rendered
configs: assets, deep links, client-side navigation, config.js and
images all resolve; default deployment output is functionally
identical to before.
@chai-guy-7 chai-guy-7 requested a review from a team as a code owner June 3, 2026 14:46
- prepend the base path to the not-found URL rewrite so the restored
  address stays inside the app prefix (refresh no longer 404s under
  a subpath deployment), with regression tests for root and subpath
- preserve the query string on the bare-prefix 301 redirect
  (e.g. /explorer?network=... no longer loses its params)
- fail fast at container start when BASE_PATH contains characters
  outside the documented charset instead of rendering a broken or
  unsafe nginx config
- cross-reference the nginx templates so header/gzip changes do not
  silently miss subpath deployments, and document the publicAsset
  convention for contributors
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.

2 participants