Deploy Web
Deploy the TanStack Start web app to Cloudflare Workers
Deploy Web (Cloudflare Workers)
EasyStarter's web app is built with TanStack Start and runs in SSR mode on Cloudflare Workers. The Web Worker connects to the Server Worker via Service Bindings — no public network hop required.
You must complete the Server deployment before deploying the web app. Service Bindings require the Server Worker to already exist.
EasyStarter supports two deployment methods — choose the one that fits your workflow:
| Method | Best for |
|---|---|
| Option 1: Local CLI | Quick launch, one-off deploys, full manual control |
| Option 2: GitHub auto-deploy | Continuous delivery, team collaboration, deploy on push |
Option 1: Local CLI deploy
Authenticate Wrangler locally, then run the deploy commands manually.
npx wrangler loginEnvironment variable overview
All web app variables are VITE_-prefixed public config — Vite injects them into the client bundle at build time. There are no secrets; everything goes in wrangler.jsonc vars.
| Type | File | Description |
|---|---|---|
| Local development | apps/web/.env.development | Auto-loaded by wrangler dev, points to localhost |
| Production deploy | apps/web/wrangler.jsonc → vars | Compiled into the Worker bundle at deploy time |
The web app has no
.env.productionsecrets and does not usewrangler secret bulk— all variables are public values.
Update apps/web/wrangler.jsonc
Fill in the Worker name, Server Service Binding, and public variables:
{
"name": "your-web-worker", // Web Worker name — must be globally unique
"compatibility_date": "2025-09-02",
"compatibility_flags": ["nodejs_compat"],
"main": "@tanstack/react-start/server-entry",
"assets": {
"not_found_handling": "single-page-application"
},
"services": [
{
"binding": "API_SERVICE",
"service": "your-server-worker" // must exactly match the name in Server's wrangler.jsonc
}
],
"vars": {
"VITE_SERVER_URL": "https://your-server-worker.your-subdomain.workers.dev",
"VITE_APP_URL": "https://your-web-worker.your-subdomain.workers.dev"
}
}| Field | Description |
|---|---|
name | Web Worker name — determines the default access URL |
services[0].service | Must exactly match the Server's name — the Service Binding won't work otherwise |
vars.VITE_SERVER_URL | Server address — used by the oRPC client and Better Auth client |
vars.VITE_APP_URL | Web app's own address — used for SEO canonical URLs, OAuth callbacks, etc. |
Deploy the Web Worker
pnpm deploy:webWrangler runs vite build to produce the SSR output, then publishes it to Cloudflare Workers.
On success, the console prints the Worker URL:
Deployed your-web-worker triggers:
https://your-web-worker.your-subdomain.workers.devVerify the deployment
Visit the Web Worker URL and confirm the following work correctly:
- Homepage loads
- User sign-up / sign-in (email + OAuth)
- Payment checkout flow (requires Stripe to be configured)
- File uploads (requires R2 to be configured)
Option 2: GitHub auto-deploy
Connect your GitHub repository to Cloudflare so every push to the target branch automatically triggers a build and deploy.
The web app has no secrets, so GitHub auto-deploy is simpler than the server — just connect the repo and fill in the build config.
Connect your GitHub repository
- Go to Cloudflare Dashboard → Workers & Pages
- Click Create → Workers → Connect to Git
- Authorize Cloudflare to access your GitHub account and select your repository
- Choose the deployment branch (usually
main)
Enter the build configuration
Fill in the following settings on the build configuration page:
| Field | Value |
|---|---|
| Root directory | / |
| Build command | pnpm --filter web build |
| Deploy command | pnpm --filter web run deploy |
| Version command | pnpm --filter web run deploy |
Root directory is
/because this is a monorepo — pnpm workspaces must resolve dependencies from the repo root.
Once configured, every push to the target branch automatically triggers a new build and deployment. View the status and logs for each deploy under Workers & Pages → your Worker → Deployments.
Custom domain (recommended)
- Go to Cloudflare Dashboard → Workers & Pages → select your Web Worker
- Go to Settings → Domains & Routes
- Click Add Custom Domain and enter your domain (must be hosted on Cloudflare), e.g.
app.yourdomain.com - Once bound, update the related config as described below
Why multiple files need to be updated
VITE_APP_URL and VITE_SERVER_URL live in wrangler.jsonc vars and are compiled into the Worker bundle at build time. Changing them requires a redeploy to take effect.
The server also references WEBSITE_URL (Better Auth trusted origins). If the web domain changes, the server config must be updated too — otherwise auth requests will be blocked by CORS.
Files to update
① apps/web/wrangler.jsonc
"vars": {
"VITE_SERVER_URL": "https://api.yourdomain.com", // matches the server custom domain
"VITE_APP_URL": "https://app.yourdomain.com" // the new web custom domain
}② apps/server/wrangler.jsonc
"vars": {
"WEBSITE_URL": "https://app.yourdomain.com", // sync to the new web domain
"SERVER_URL": "https://api.yourdomain.com"
}Redeploy to apply the changes
Both apps have changes, so deploy both:
pnpm deployThis runs pnpm deploy:server followed by pnpm deploy:web.
varsare static config compiled into the bundle. Every change towrangler.jsoncvarsrequires a redeploy — updating secrets alone will not apply these changes.