The problem
SSR HTML always renders in the base locale. /fr pages come back in English. The client hydrates and flashes to French a moment later.
paraglideMiddleware in src/server.ts should set the locale before rendering. It never runs.
The cause
@cloudflare/vite-plugin reads the main field from wrangler.jsonc to resolve the server entry. The standard Cloudflare + TanStack Start docs say to set:
{ "main": "@tanstack/react-start/server-entry" }
That's TanStack Start's default handler. It doesn't know about your src/server.ts. Your middleware is dead code.
The fix
{ "main": "./src/server.ts" }
Now the Cloudflare plugin loads your file, paraglideMiddleware runs, getLocale() returns the right locale during SSR.
server.ts
import { paraglideMiddleware } from "./paraglide/server.js"
import handler from "@tanstack/react-start/server-entry"
export default {
fetch(req: Request): Response | Promise<Response> {
return paraglideMiddleware(req, () => handler.fetch(req))
},
}
Pass the original req to handler.fetch — not the middleware's delocalized request. TanStack Router handles URL rewriting via deLocalizeUrl/localizeUrl in its rewrite config. Passing the delocalized request causes redirect loops.
Why it's easy to miss
TanStack Start auto-discovers src/server.ts via its own Vite plugin. But the Cloudflare Vite plugin has its own entry resolution that takes precedence. The issue only appears when both plugins are in play.