Skip to content

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

Open
fabiovincenzi wants to merge 31 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 31 commits into
anomalyco:devfrom
fabiovincenzi:feat/base-path-support

Conversation

@fabiovincenzi

@fabiovincenzifabiovincenzi 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
fabiovincenziforce-pushed the feat/base-path-support branch 2 times, most recently from 19ffabd to e2c6334CompareMay 29, 2026 11:42
@fabiovincenzi
fabiovincenziforce-pushed the feat/base-path-support branch from f5d31a8 to 9dc842cCompareJune 1, 2026 09:27
@KaminariOS

Copy link
Copy Markdown

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

@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:

constgetCurrentUrl=()=>{letbasePath=window.__OPENCODE_BASE_PATH__||import.meta.env.VITE_OPENCODE_SERVER_BASE_URL||""if(basePath&&!basePath.startsWith("/"))basePath="/"+basePathbasePath=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){constbaseUriPath=newURL(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}`returnlocation.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 💯

@KaminariOS

Copy link
Copy Markdown

Why is this PR still not merged?

It is a Pareto improvement.

@stackedsax

Copy link
Copy Markdown

@adamdotdevin, @Hona, @Brendonovich, anything we can do to keep this moving?

@xieyucheng123

Copy link
Copy Markdown

Bug: Most frontend API requests ignore --base-path prefix

When running opencode serve --base-path /opencode, most frontend API requests do not include the base path prefix, causing them to fail behind a reverse proxy.

Reproduction

# Start server with base path
opencode serve --hostname 0.0.0.0 --port 4096 --base-path /opencode
# Open http://localhost:4096/opencode/ in a browser# Open DevTools → Network tab

Observed behavior

Only 2 out of 9 unique API endpoints use the base path prefix:

EndpointUses /opencode prefix?
/opencode/api/session?limit=5000&order=desc✅ Yes
/opencode/global/health✅ Yes
/api/health❌ No
/api/event (SSE, polled continuously)❌ No
/api/session/active❌ No
/api/project❌ No
/api/provider❌ No
/api/model❌ No
/api/model/default❌ No
/global/health (initial load)❌ No

Impact

Behind a reverse proxy (e.g. nginx routing /opencode/ → opencode backend), the unprefixed requests hit the wrong backend or return 404. The frontend cannot function properly.

When accessing the server directly (no proxy), the unprefixed requests still work because the server handles both prefixed and unprefixed routes — so this bug is only visible behind a reverse proxy.

Environment

  • Built from this PR branch (feat/base-path-support)
  • Browser: Chrome
  • --base-path /opencode

Suggested fix

The base path is correctly applied to the SolidJS Router and some API calls (api/session, global/health), but the majority of API client calls are not using the base path. It looks like the base path injection in the frontend API client is only partially applied — likely some code paths use window.__OPENCODE_BASE_PATH__ while others construct URLs without it.

@opencode-agent

Copy link
Copy Markdown
Contributor

User xieyucheng123 does not have write permissions

github run

Sign up for freeto 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

6 participants

@fabiovincenzi@KaminariOS@awschmeder@bcgrillo@stackedsax@xieyucheng123