China domain routing
How today.ai redirects mainland China page navigations without intercepting embed, API, or resource requests.
The Vercel project redirects eligible mainland China page navigations from
today.ai to todayai.cn. This rule is deliberately narrower than a generic
geo redirect: it must never capture API traffic, embed bootstrap traffic, or
page subresources.
The redirect matcher lives in apps/web/vercel.ts. Shared bypass names and
values live in apps/web/domain-routing.mjs.
Redirect decision
A request redirects only when every positive condition is true and no bypass is present:
(mainland China OR explicit simulation)
AND explicitly allowlisted public entry path
AND Sec-Fetch-Dest: document
AND Sec-Fetch-Mode: navigate
AND no session cookie
AND no bypass header
AND no bypass cookie
AND no bypass query
AND not continuing a same-origin navigation on today.aiThe two Fetch Metadata headers are the primary request-type boundary:
Sec-Fetch-Dest: documentidentifies a top-level document. Subframes useiframe, and resources use destinations such asimage,script,style, orfont.Sec-Fetch-Mode: navigateconfirms browser navigation rather thanfetch, XHR, or a resource load.
Both headers are required. If either is absent, malformed, or carries another
value, the request stays on today.ai. This fail-safe behavior supports older
WebViews and non-browser clients without guessing from Accept, a filename
extension, or a user agent.
Sec-Fetch-User is not part of the rule. It is present only for some
user-activated navigations, so requiring it would miss address-bar entry,
reloads, redirects, and programmatic navigations that are still valid page
loads.
Public entry allowlist
Only these exact paths can trigger the redirect (with an optional trailing slash):
//landing/waitlist/downloads
Every other path stays on its original origin by default. This includes login,
OAuth authorization and callbacks, password recovery, invitations, authenticated
app pages, shared content, SEO articles, embeds, APIs, and static resources.
Unknown routes and descendants such as /downloads/child are not eligible.
Adding a new page must not implicitly opt it into a cross-domain redirect.
Native authentication can start without a same-origin referrer or bypass cookie. Its login and OAuth URLs must therefore remain outside the allowlist regardless of Fetch Metadata. The top-level document check alone cannot distinguish an OAuth navigation from a public landing-page visit.
Bypasses
Any bypass keeps the request on today.ai:
- Presence of
better-auth.session_token,__Secure-better-auth.session_token, or__Secure-today.desktop_sessionbypasses the redirect. Only the exact cookie name matters: empty, expired, and unverified values also bypass. This is a routing hint; normal authentication still validates the session. x-today-bypass-geo-redirect: trueis the explicit per-request bypass for automation and controlled callers.__Host-today-geo-redirect-bypass=1is the host-wide routing bypass. The current Web application does not write this cookie.bypass_geo_redirect=1is the URL-scoped bypass for links that must remain ontoday.ai. It affects only request URLs that carry it and does not create persistent browser state.- A
Refererbeginning withhttps://today.ai/keeps subsequent same-origin navigation on the international site.
The embed path itself is excluded from the redirect, so its document and subresources remain on their original origin without relying on a cookie.
The cookie is a host-only, full-site session cookie:
__Host-today-geo-redirect-bypass=1; Path=/; Secure; HttpOnly; SameSite=LaxPath=/ makes it available to every request on the host. The __Host- prefix
requires Secure, forbids Domain, and requires Path=/, preventing a
subdomain or narrower path from changing its scope. It has no Expires or
Max-Age, so it ends with the WebView or browser session.
This is routing state, not authentication. Vercel consumes it when evaluating
the redirect. Do not reuse embed_bearer: that JWT remains scoped to /api/
and must not be broadened to the whole site.
Why classification happens on the request
Vercel must decide whether to redirect before the application produces a
response, so response Content-Type cannot classify the request. The request's
Fetch Metadata is the closest browser-provided description of the navigation
context and is available at the redirect decision point.
Do not replace it with Accept: text/html, file-extension allowlists, or user
agent detection. Those are hints rather than navigation identity: APIs may
accept HTML, extensionless resources are common, and user agents are neither
complete nor reliable.
Validation
The configuration tests cover geo and simulation rules, both Fetch Metadata requirements, incomplete headers, common resource destinations, iframe navigations, bypasses, the public entry allowlist, and protected and unknown paths:
pnpm --filter @todayai-labs/web exec vitest run \
scripts/vercel-config.test.ts src/proxy.test.tsFor an end-to-end simulation, add all three request headers to a top-level page navigation:
x-today-simulate-geo-redirect: true
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigateAn allowlisted public entry page should redirect to https://todayai.cn. Login,
OAuth, callback, and unknown paths must stay on today.ai with the same headers.
Changing the destination to image, changing the mode to cors, removing either
Fetch Metadata header, or adding any bypass must keep the request on today.ai.
Backend domains
Dev and production backend domain resolution in packages/auth-client.
Environment variables
How env vars flow through Next.js in this repo — `.env` file layout, the `NEXT_PUBLIC_*` inlining rule that breaks our shared utilities if violated, the three-tier domain values, and the variable reference.