Back to blog

SSR Locale Broken? Check Your Wrangler Entry Point

One line in wrangler.jsonc that makes or breaks your i18n

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.