Skip to content

feat(server): runtime base path support for reverse proxy deployments - #28326

Open
fabiovincenzi wants to merge 27 commits into
anomalyco:devfrom
fabiovincenzi:feat/base-path-support
Open

feat(server): runtime base path support for reverse proxy deployments#28326
fabiovincenzi wants to merge 27 commits into
anomalyco:devfrom
fabiovincenzi:feat/base-path-support

Conversation

@fabiovincenzi

@fabiovincenzi fabiovincenzi commented May 19, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #7624

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds a --base-path flag (also server.basePath in config) so opencode web works behind a reverse proxy at a prefix like platform.com/opencode/.
The server strips the prefix before routing, redirects /opencode to /opencode/, and returns 404 for anything outside the prefix.
The UI picks up the base path via an injected global so the SolidJS router and API calls use the right URLs.
Vite base changed to "./" for relative chunk imports.
Supersedes #7625, #18209, #25513, #23912.

How did you verify your code works?

Built with bun run --cwd packages/opencode build -- --single, ran opencode web --base-path /opencode.
UI loads, assets resolve, API works under prefix, 404 outside it, 301 redirect without trailing slash, no breakage without the flag.
18 unit tests covering normalization, HTML injection, XSS prevention, and CSP hash computation.

Screenshots / recordings

N/A - no visual changes, only routing behavior.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Related PRs Found

This PR (28326) supersedes several earlier attempts at solving the same problem, as noted in the description. Here are the related PRs:

  1. feat: base path support #7625 - "feat: base path support"
  2. feat: support serving opencode from a subpath #25513 - "feat: support serving opencode from a subpath"
  3. feat: make opencode web embeddable in iframes at a subpath #23912 - "feat: make opencode web embeddable in iframes at a subpath"
  4. feat: App - Support setting base URL during build #18209 - "feat: App - Support setting base URL during build"

Why they're related:
All of these PRs attempt to solve the same problem of supporting base path/subpath deployment. According to the PR description, PR #28326 is the comprehensive solution that supersedes the earlier attempts because:

This PR consolidates all the learnings into a complete implementation with both CLI flag support, server-side request interception, HTML injection, and frontend SolidJS router integration.

@fabiovincenzi
fabiovincenzi force-pushed the feat/base-path-support branch 2 times, most recently from 19ffabd to e2c6334 Compare May 29, 2026 11:42
@fabiovincenzi
fabiovincenzi force-pushed the feat/base-path-support branch from f5d31a8 to 9dc842c Compare June 1, 2026 09:27
@KaminariOS

Copy link
Copy Markdown

A really useful feat.
I hope this PR gets merged ASAP.

@thdxr
thdxr requested review from Brendonovich and Hona as code owners June 11, 2026 00:18
@awschmeder

Copy link
Copy Markdown

Hello @fabiovincenzi -- I found your open PR while trying to solve a similar issue where the reverse proxy injects a <base href> tag (e.g., JupyterHub via jupyter-server-proxy) without the operator needing to pass --base-path.

Use case

JupyterHub's jupyter-server-proxy automatically injects <base href="/user/<username>/opencode/"> into proxied HTML responses. There's no easy way to pass --base-path at launch time in all JupyterHub deployments. Currently even with your PR, those users would still see 404s unless they configure --base-path.

Proposed addition to packages/app/src/entry.tsx

In getCurrentUrl(), after the existing window.__OPENCODE_BASE_PATH__ / env var resolution, add a fallback that reads document.baseURI when basePath is still empty:

const getCurrentUrl = () => {
  let basePath = window.__OPENCODE_BASE_PATH__ || import.meta.env.VITE_OPENCODE_SERVER_BASE_URL || ""
  if (basePath && !basePath.startsWith("/")) basePath = "/" + basePath
  basePath = basePath.replace(/\/+$/, "")
  // When no explicit base path is configured, check whether a <base href> tag was injected
  // by an upstream reverse proxy (e.g., jupyter-server-proxy for JupyterHub). If document.baseURI
  // diverges from the origin root, the proxy mount point is the correct API base.
  if (!basePath) {
    const baseUriPath = new URL(document.baseURI).pathname.replace(/\/+$/, "")
    if (baseUriPath && baseUriPath !== "/") basePath = baseUriPath
  }
  if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
  if (import.meta.env.DEV)
    return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}${basePath}`
  return location.origin + basePath
}

Priority order:

  1. window.__OPENCODE_BASE_PATH__ (set by --base-path, most explicit -- your existing logic)
  2. VITE_OPENCODE_SERVER_BASE_URL (build-time env var -- your existing logic)
  3. document.baseURI pathname (proxy-injected <base href>, zero-config fallback -- proposed addition)
  4. Empty string (origin root, original behavior)

No behavior change for direct access (baseURI pathname is /) or --base-path users (fallback never runs). Only activates when a proxy has already injected a <base href> tag.

Happy to open a PR against your branch if that's easier. Thanks for the great work on the full solution!

@fabiovincenzi

Copy link
Copy Markdown
Author

Hey @awschmeder, great suggestion! I've implemented exactly this, the document.baseURI fallback is now added in both getCurrentUrl() and the SolidJS Router base prop. The priority order is preserved as you described.

Thanks for the detailed write-up, it made the implementation straightforward!

@fabiovincenzi

Copy link
Copy Markdown
Author

Hi @Hona @Brendonovich, this PR adds base path support for reverse proxy deployments. Would love your feedback when you have a moment!

@bcgrillo

Copy link
Copy Markdown

This feature is really useful! I hope they include it. For now, I’m testing it on your branch. Thanks for all your hard work @fabiovincenzi and @awschmeder 💯

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.

[FEATURE]: Base path / prefix routing support

4 participants