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 or *.pages.dev URLs are hard to remember and share.
  • Payment callbacks need public URLs: Stripe and Creem must reach your backend from the public internet.
  • SEO metadata should not point at preview domains: sitemap, canonical tags, and robots should use your real production domain.

node3-pay-service must be bound first because it receives payment provider callbacks. Without a custom domain, Stripe and Creem webhooks are usually unreliable.

web currently deploys to a Worker, not Pages. Bind your custom domain in Cloudflare Dashboard -> Workers & Pages -> your web Worker -> Settings -> Domains & Routes.

Recommended workflow: Copy apps/web to a new project and customize there. Do not modify apps/web directly. Treat it as the official template.

Use this order in the current system:

  1. Create the tenant in Admin first

    • Open Admin -> Projects
    • Create a new project row with a unique app_key
    • This registers the tenant in t_project
  2. Copy apps/web to a new app directory

    • Example: apps/my-brand
  3. Edit apps/my-brand/zship.app.json

    • appKey: must match the tenant you created in Admin
    • domain: your production domain
    • siteUrl: your canonical production URL
    • pagesProject: the deployment target name for this frontend app
    • Brand fields such as siteName, brand.name, and brand.logo
  4. Keep runtime env aligned

    • The frontend still reads APP_KEY at runtime today
    • Dev Console syncs env from zship.app.json, but env is still part of the runtime path
  5. Change wrangler.toml

    • Update the name field
    • If you keep name = "web" in the copied app, deployment may overwrite the original web Worker
  6. Bind the new custom domain

    • Give the copied frontend its own domain
    • Do not reuse the original web domain unless you intentionally want to replace it
  7. Redeploy the copied frontend

    • Treat it as its own frontend Worker and deploy it independently

Mental model:

  • Admin creates the tenant
  • zship.app.json maps a frontend app to that tenant
  • wrangler.toml decides which Worker receives the deployment

admin usually deploys to Pages. Bind the 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. Otherwise uploads may succeed while public URLs return 404. Full steps are in Troubleshooting.

2. How zship.app.json, site.ts, and env work together

Section titled “2. How zship.app.json, site.ts, and env work together”

Frontend configuration is layered:

  1. zship.app.json

    • Recommended source of truth for frontend app identity
    • Manages appKey, siteUrl, domain, brand metadata, and deployment target names
  2. site.ts

    • Still used by the frontend runtime as typed site configuration
    • Dev Console syncs manifest values into this file
  3. Environment variables

    • Local: .env
    • Production: Cloudflare Variables and Secrets
    • The frontend still reads APP_KEY, SITE_URL, and service URLs from env at build/SSR/runtime boundaries

So the practical rule today is:

  • Edit zship.app.json
  • Let Dev Console sync to site.ts and .env
  • Remember that runtime still reads env

Do not treat these as the same thing:

  • Canonical URL: your real production site URL, for example https://my-brand.com
  • Latest deployed URL: the latest preview or temporary URL discovered during deploy, for example https://xxxx.my-brand.pages.dev

Current rule:

  • siteUrl in zship.app.json is the canonical URL
  • SITE_URL should follow that canonical URL
  • the latest deployed pages.dev URL is only a deploy artifact and should not replace sitemap, robots, or canonical metadata

For a copied web project, you will usually configure values like:

APP_KEY=my-brand
SITE_URL=https://my-brand.com
AUTH_SERVICE_URL=https://n1.example.com
PAY_SERVICE_URL=https://n3.example.com
BLOG_API_URL=https://n5.example.com
SITE_SERVICE_URL=https://n7.example.com
CHECKIN_SERVICE_URL=https://n9.example.com
AI_SERVICE_URL=https://n10.example.com
SUPPORT_SERVICE_URL=https://n2.example.com

Important:

  • APP_KEY must match the tenant created in Admin
  • SITE_URL should be the real production URL, not a temporary pages.dev preview URL

After changing env, custom domains, or frontend identity, redeploy the frontend. Otherwise old values may still be baked into the build output.

Before initialization, make sure:

  • The public site opens under the correct production domain
  • Admin opens under the correct production domain
  • Login and registration target the correct tenant
  • Frontend requests hit the real production backend URLs
  • Sitemap, robots, and canonical metadata point at the production domain rather than pages.dev