EasyStarter logoEasyStarter

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:

MethodBest for
Option 1: Local CLIQuick launch, one-off deploys, full manual control
Option 2: GitHub auto-deployContinuous delivery, team collaboration, deploy on push

Option 1: Local CLI deploy

Authenticate Wrangler locally, then run the deploy commands manually.

npx wrangler login

Environment 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.

TypeFileDescription
Local developmentapps/web/.env.developmentAuto-loaded by wrangler dev, points to localhost
Production deployapps/web/wrangler.jsoncvarsCompiled into the Worker bundle at deploy time

The web app has no .env.production secrets and does not use wrangler secret bulk — all variables are public values.

Update apps/web/wrangler.jsonc

Fill in the Worker name, Server Service Binding, and public variables:

apps/web/wrangler.jsonc
{
  "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"
  }
}
FieldDescription
nameWeb Worker name — determines the default access URL
services[0].serviceMust exactly match the Server's name — the Service Binding won't work otherwise
vars.VITE_SERVER_URLServer address — used by the oRPC client and Better Auth client
vars.VITE_APP_URLWeb app's own address — used for SEO canonical URLs, OAuth callbacks, etc.

Deploy the Web Worker

pnpm deploy:web

Wrangler 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.dev

Verify 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

  1. Go to Cloudflare DashboardWorkers & Pages
  2. Click CreateWorkersConnect to Git
  3. Authorize Cloudflare to access your GitHub account and select your repository
  4. Choose the deployment branch (usually main)

Enter the build configuration

Fill in the following settings on the build configuration page:

FieldValue
Root directory/
Build commandpnpm --filter web build
Deploy commandpnpm --filter web run deploy
Version commandpnpm --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.


  1. Go to Cloudflare Dashboard → Workers & Pages → select your Web Worker
  2. Go to Settings → Domains & Routes
  3. Click Add Custom Domain and enter your domain (must be hosted on Cloudflare), e.g. app.yourdomain.com
  4. 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 deploy

This runs pnpm deploy:server followed by pnpm deploy:web.

vars are static config compiled into the bundle. Every change to wrangler.jsonc vars requires a redeploy — updating secrets alone will not apply these changes.