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.
0. Why bind domains?
Section titled “0. Why bind domains?”The system may run without binding custom domains. But:
- URLs will be ugly — default
*.workers.devURLs are hard to remember and share. *.workers.devis 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.
1. Bind custom domains
Section titled “1. Bind custom domains”Web (Astro 6)
Section titled “Web (Astro 6)”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.
Backend Workers
Section titled “Backend Workers”Priority: node3-pay-service — bind it first so Stripe and Creem webhooks can reach your payment callback endpoint. Other Workers can follow.
CDN and R2 (CDN_PUBLIC_URL)
Section titled “CDN and R2 (CDN_PUBLIC_URL)”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”:
- TypeScript defaults — e.g.
apps/web/src/config/site.tsholds branding,siteUrl, and readsimport.meta.env.APP_KEY || 'demo'for the tenant key used in auth cookies and API proxies. - Environment variables — locally in
apps/web/.env(not committed with secrets), and in production under Cloudflare Variables and Secrets for thewebWorker (or Pages, if you use that layout). Values such asAUTH_SERVICE_URL,APP_KEY,ZSHIP_KEY, andSITE_URLare read at build/SSR time viaimport.meta.env(andastro.config.mjsmay readprocess.env.SITE_URL).
So APP_KEY in env overrides the 'demo' fallback — site.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.
Recommended values for web
Section titled “Recommended values for web”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
admin project
Section titled “admin project”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.
5. The simplest sanity check
Section titled “5. The simplest sanity check”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.