Route /api/auth through the same origin in both dev and prod so cookies stay first-party and the browser and React Router SSR share one auth surface. - Vite dev server proxies /api/auth to the Convex HTTP site. - Better Auth uses secure cookies when the site URL is https. - Add docs/auth-proxy.md documenting the required production ingress (Caddy/Traefik) and Convex SITE_URL / CONVEX_SITE_URL setup.
2.5 KiB
2.5 KiB
Auth Proxy — Production Ingress Requirement
The application uses same-origin authentication: the browser and React Router SSR both hit
/api/auth/* on the public application domain. This keeps cookies first-party, avoids
cross-origin credentials, and gives development and production the same API surface.
Required production route
At the public application domain, route:
| Prefix | Target |
|---|---|
/api/auth/* |
Convex HTTP site |
/* |
React Router frontend |
Caddy
zopu.example.com {
handle /api/auth/* {
reverse_proxy https://befitting-dalmatian-161.convex.site {
header_up Host befitting-dalmatian-161.convex.site
}
}
handle {
reverse_proxy frontend:3000
}
}
Dokploy / Traefik
Create a higher-priority path router for /api/auth that forwards to the external Convex
site URL. Ensure it:
- Preserves the original browser
Cookieheader - Passes
Set-Cookieresponses back (rewrite domain if Convex emits an explicit one) - Preserves the original method and body
- Forwards
X-Forwarded-HostandX-Forwarded-Proto - Passes the full path unchanged (e.g.
/api/auth/convex/token) - Does not cache auth responses
- Allows OAuth callback routes under the same prefix
Convex environment
The Convex deployment SITE_URL must match the public origin users visit, not the Convex
site URL:
# Production
npx convex env set SITE_URL 'https://zopu.example.com'
# Local
npx convex env set SITE_URL 'http://100.101.157.28:5173'
# Staging
npx convex env set SITE_URL 'https://zopu.cheaptricks.puter.wtf'
This value populates Better Auth's trustedOrigins. The Convex deployment also uses
CONVEX_SITE_URL as the internal baseURL for route registration; that value is the HTTP
site URL (e.g. https://befitting-dalmatian-161.convex.site).
Why same-origin
- First-party cookies. No
SameSite=None, no third-party cookie restrictions. - SSR consistency. The React Router server uses the same auth surface the browser sees; no cross-host credential translation.
- No CORS complexity. The browser talks only to its own origin.
- The reverse proxy handles the cross-host hop server-side.
Related
- Better Auth: Trusted Origins
apps/web/vite.config.ts— Vite dev proxy (/api/auth→ Convex site)packages/auth/src/web/auth-client.ts—window.location.originapps/web/src/lib/auth.server.ts— SSR token loader via request origin