get-convex / get-convex/better-auth

react-start proxy sends x-forwarded-host, which Convex's edge resolves as a deployment name — every /api/auth/* returns an empty 404

Open
#424 2 comments 4 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
764
Forks
126
PR merge metrics
No merged PRs in 30d

Description

## Summary

Every `/api/auth/*` request through the TanStack Start proxy started returning an empty 404. Production and local dev broke at the same moment, with no deploy and no dependency change on our side.

`handler()` in `dist/react-start/index.js` sets `x-forwarded-host` to the app's own host. Convex's edge resolves _which deployment serves a request_ from that header when it is present, so it looks for a deployment named `example.com`, finds none, and answers 404 before any deployment code runs.

## Reproduction

Three requests against any deployment with Better Auth routes registered — substitute `` with your own:

```bash
# 1. Baseline — works
curl -s -o /dev/null -w '%{http_code}\n' \
https://.convex.site/api/auth/ok
# 200

# 2. Same request plus the header the proxy adds
curl -s -o /dev/null -w '%{http_code}\n' \
https://.convex.site/api/auth/ok \
-H 'x-forwarded-host: example.com'
# 404, empty body, no content-type

# 3. Proof the header drives deployment routing: a hostname that does not
# exist, pointed by the header at one that does
curl -s -o /dev/null -w '%{http_code}\n' \
https://this-does-not-exist-999.convex.site/api/auth/ok \
-H 'x-forwarded-host: .convex.site'
# 200
```

Any value other than the deployment's own `.convex.site` name gives 404 — including that same deployment's `.convex.cloud` URL, `localhost:3000`, and an unrelated subdomain.

The two 404 shapes tell the layers apart:

| Case | Response |
| ------------------------------- | ------------------------------------------ |
| Unknown deployment | `content-length: 0`, no `content-type` |
| Known deployment, unknown route | `content-type: text/plain`, non-empty body |

The failure returns the first shape, so it happens before the deployment's HTTP router runs.

## Impact

- Every auth route 404s: `get-session`, `sign-in/social`, `convex/token`.
- Both `handler()` (`dist/react-start/index.js`) and `getToken()` (`dist/utils/index.js`) are affected. `getToken` never sets the header itself, but it forwards the browser request's headers verbatim, and platforms like Vercel set `x-forwarded-host` on the inbound request.
- Local dev fails identically, with `x-forwarded-host: localhost:3000`.
- No deploy is needed to trigger it. Deployments that had been serving fine for months broke in place.
- `dist/nextjs/index.js` sets the same header, so the Next.js integration looks exposed too (not verified).

## Workaround

We patched the package to drop the header on the way out. The value still reaches Better Auth: `x-better-auth-forwarded-host` carries it, and `restoreOriginalForwardedHeaders()` in `dist/client/create-client.js` writes it back to `x-forwarded-host` inside Convex.

```diff
# dist/react-start/index.js — handler()
- headers.set("x-forwarded-host", requestUrl.host);
+ headers.delete("x-forwarded-host");
```

```diff
# dist/utils/index.js — getToken()
headers.set("host", new URL(siteUrl).host);
+ const forwardedHost = headers.get("x-forwarded-host");
+ if (forwardedHost) {
+ headers.set("x-better-auth-forwarded-host", forwardedHost);
+ headers.delete("x-forwarded-host");
+ }
```

Deleting rather than skipping matters — the inbound request already carries the header on Vercel, so not setting it is not enough.

## Suggested fix

Since #327 already routes the original host through `x-better-auth-forwarded-host`, sending the generic header on this hop looks redundant. Dropping it in both call sites (and in the Next.js handler) restores the contract that `restoreOriginalForwardedHeaders()` was written for.

## Environment

- `@convex-dev/better-auth` 0.12.2 — 0.12.5 still sets the header
- `better-auth` 1.6.11
- `convex` 1.43.0
- TanStack Start 1.168.5 on Vercel, and local dev (Vite)

## Timeline

Broke on 2026-08-12 at roughly 00:15 UTC. Our lockfile had not changed since 2026-08-07 and nothing was deployed on either side, so the change appears to be in Convex's edge routing rather than in this package.

Contributor guide

Open the contributing guide

Research direction

Start by inspecting handler() in dist/react-start/index.js and getToken() in dist/utils/index.js, then compare the related Next.js handler in dist/nextjs/index.js and restoreOriginalForwardedHeaders() in dist/client/create-client.js. Reproduce the header-dependent 404 with the listed curl requests and verify that auth routes work with forwarded headers preserved through the Better Auth-specific header.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, authentication
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.