Skip to content

fix(self-host): stop rejecting dashboard reads behind an HTTPS proxy - #2246

Open
zackleman wants to merge 3 commits into
UsefulSoftwareCo:v2from
zackleman:self-host-https-origin
Open

zackleman wants to merge 3 commits into
UsefulSoftwareCo:v2from
zackleman:self-host-https-origin

Conversation

@zackleman

@zackleman zackleman commented Oct 9, 2026 •

Copy link
Copy Markdown

Summary

Fixes #2199.

Behind a proxy that ends TLS, such as Railway's edge or the HTTPS reverse proxy the README recommends, every dashboard batch returns 403. The browser then fails every read in that batch, so any page that loads two or more things at once shows "Unable to complete this request".

The host reaches workerd over plain HTTP, and ReverseProxy's Rewrite drops the X-Forwarded-Proto the TLS proxy sent. HttpServerRequest.toURL then builds http://<host>, which never matches the browser's Origin: https://<host> in the batch same-origin check (batch-host.ts).

 productProxy Rewrite
   request.Out.Header.Set("x-executor-client-ip", address)
+  if publicHTTPS                       // BETTER_AUTH_URL is https://
+    request.Out.Header.Set("X-Forwarded-Proto", "https")

The scheme comes from the configured origin, not from the client. Rewrite still drops a client's own X-Forwarded-Proto.

Evidence

  • Before: a Railway deployment of 2.0.0-beta.12 on a custom HTTPS domain. Over an hour of normal use, none of 33 POST /api/dashboard/batch requests succeeded (28 returned 403). curl with Origin: https://<domain> gets 403. With Origin: http://<domain> it passes the check (400 for an empty body).
  • After: a linux/arm64 build of this host, mounted into the 2.0.0-beta.12 image with BETTER_AUTH_URL=https://executor.test. Origin: https://executor.test passes the check (400 for an empty body). Origin: http://executor.test gets 403.
  • New TestProxyForwardsTheConfiguredOriginsScheme fails without the header line (workerd saw "", want "https") and passes with it.
  • Updated to v2 at 0e9e2c6b3. That build still drops the header. compress_test.go, added since, now passes the new flag to productProxy. go vet ./... and go test ./... in apps/hosted/self-host/native pass.
  • In production on Railway, workerd's injectRequestHeaders option sets the same header on the product socket. That stand-in has kept the dashboard working from 2.0.0-beta.12 through 2.0.0-beta.18, which supports this fix in the host.

No E2E scenario serves the dashboard behind a TLS proxy. The Go suite runs in self-host-native.

none

Merge Danger

Door: two-way

Blast Radius: self-host servers with an HTTPS BETTER_AUTH_URL.

Every request to workerd now carries X-Forwarded-Proto: https. That is true for every origin a browser can sign in on. On such a server, a dashboard opened over plain HTTP (for example http://localhost:4400) now gets 403 on batches. Sign-in already fails there.

🤖 Generated with Claude Code

A proxy that ends TLS, such as Railway's edge or the reverse proxy the README
recommends, reaches the host over plain HTTP. ReverseProxy's Rewrite drops the
X-Forwarded-Proto it sends, so the product builds http:// request URLs and
rejects every dashboard batch from an https:// page with 403, failing each
read in it.

When the configured origin is HTTPS, the proxy now sets X-Forwarded-Proto: https
itself. A client's own header is still dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zackleman zackleman reopened this Oct 14, 2026

This branch has not been deployed

No deployments
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.

1 participant