Skip to content

05. Bind Domains and Environment Variables

This chapter is where your deployment starts looking like a real online product instead of a set of temporary project URLs.

The system may run without binding custom domains. But:

  • URLs will be ugly — default *.workers.dev URLs are hard to remember and share.
  • *.workers.dev is internal-only — Cloudflare’s default Worker URLs are reachable mainly from within Cloudflare’s network. External callbacks (e.g. from Stripe, Creem) often fail or time out when hitting *.workers.dev.
  • You need public URLs for webhooks — Payment providers (Stripe, Creem) must POST to your backend to confirm payments. Those callbacks must reach a publicly accessible URL.

node3-pay-service must be bound — it receives payment provider callbacks. Without a custom domain, Stripe and Creem webhooks will not reach it reliably.

Web deploys to a Worker (not Pages). Bind your custom domain to the web Worker in Cloudflare Dashboard → Workers & Pages → your web Worker → Settings → Domains & Routes.

Recommended workflow: Copy apps/web to a new project (e.g. ai-saas) and customize there. Do not modify apps/web directly — treat it as the template.

Admin deploys to Pages. Bind your domain in Workers & Pages → admin → Custom domains.

Priority: node3-pay-service — bind it first so Stripe and Creem webhooks can reach your payment callback endpoint. Other Workers can follow.

If you use node6-cdn-service for uploads, the Worker’s CDN_PUBLIC_URL must match the R2 bucket custom domain, or you may see uploads succeed while public URLs 404. Full steps (first-time R2 setup in the Dashboard, routine pushes from Dev Console) are in Troubleshooting §3.

3. Change frontend environment variables to real production addresses

Section titled “3. Change frontend environment variables to real production addresses”

Once domains are known, update the frontend apps to point at the real online services.

How environment variables relate to site.ts and local .env

Section titled “How environment variables relate to site.ts and local .env”

Configuration is layered; it is not “either site.ts or env”:

  1. TypeScript defaults — e.g. apps/web/src/config/site.ts holds branding, siteUrl, and reads import.meta.env.APP_KEY || 'demo' for the tenant key used in auth cookies and API proxies.
  2. Environment variables — locally in apps/web/.env (not committed with secrets), and in production under Cloudflare Variables and Secrets for the web Worker (or Pages, if you use that layout). Values such as AUTH_SERVICE_URL, APP_KEY, ZSHIP_KEY, and SITE_URL are read at build/SSR time via import.meta.env (and astro.config.mjs may read process.env.SITE_URL).

So APP_KEY in env overrides the 'demo' fallbacksite.ts wires the app to env; it does not replace it. Some public login settings (for example a Google OAuth client id) may be loaded from the site/auth config API at runtime; you still need env for service base URLs and APP_KEY so server routes and SSR call the correct backend for your tenant.

The web project should typically be updated with:

  • real backend base URLs
  • real auth-related URLs
  • the correct app_key
  • any public site settings that still point at temporary domains

The admin app should also be switched from temporary or local values to the correct production endpoints.

4. Redeploy the frontend after environment changes

Section titled “4. Redeploy the frontend after environment changes”

Changing environment variables is not enough by itself.

After you finish those updates:

  • redeploy web
  • redeploy admin

That ensures the new production URLs are actually baked into the running build.

Run a quick end-to-end check:

  • Can you open the public site?
  • Can you open the admin?
  • Does login point at the correct domain?
  • Do frontend requests target the real production APIs instead of old temporary URLs?

If any answer is no, stop here and fix it before initialization.