Skip to content

Deploy to Cloudflare Pages

Each store is its own Cloudflare Pages project. The build bakes the brand in via PUBLIC_* environment variables, so the same commit deploys to every store by changing only the env.

Terminal window
npm install
PUBLIC_BRAND_SLUG=store-b \
PUBLIC_BRAND_NAME='Store B' \
PUBLIC_BRAND_LOGO=/images/store-b-logo.png \
PUBLIC_BRAND_PRIMARY='#0F766E' \
PUBLIC_BRAND_MENU='gear:Gear,apparel:Apparel' \
PUBLIC_API_BASE=https://api.store-b.example.com/api/v1 \
RCOMMERCE_API_URL=https://api.store-b.example.com \
PUBLIC_AIRWALLEX_ENV=demo \
PUBLIC_HERO_VIDEO_URL='https://customer-<id>.cloudflarestream.com/<video-id>/iframe?muted=true&preload=true&loop=true&autoplay=true&controls=false' \
npm run build

All PUBLIC_* values are inlined at build time — setting them only in the Pages dashboard does nothing unless the dashboard’s build environment also uses them. Simplest reliable setup: keep the full set in a per-brand wrangler.<brand>.toml (see below) and build with those same values.

Terminal window
npx wrangler pages deploy dist --project-name=<project> --branch=main

Attach the store’s domains to the project (Pages → Settings → Domains), e.g. store-a.example.com + www.store-a.example.com on my-storefront, store-b.example.com on store-b-pages.

storefront/wrangler.store-b.toml is the reference example — copy it per store and adjust:

# Store B storefront — deploys to the store-b-pages Pages project with
# Store B branding and the store-b backend. Build first with the same PUBLIC_*
# values inlined (they are baked in at build time):
#
# PUBLIC_BRAND_SLUG=store-b PUBLIC_BRAND_NAME='Store B' \
# PUBLIC_BRAND_LOGO=/images/store-b-logo.png PUBLIC_BRAND_PRIMARY=#0F766E \
# PUBLIC_BRAND_MENU='gear:Gear,apparel:Apparel' \
# PUBLIC_API_BASE=https://api.store-b.example.com/api/v1 \
# RCOMMERCE_API_URL=https://api.store-b.example.com \
# PUBLIC_AIRWALLEX_ENV=demo \
# PUBLIC_HERO_VIDEO_URL='https://…' \
# npm run build
# npx wrangler pages deploy dist --config wrangler.store-b.toml --branch=main
name = "store-b-pages"
pages_build_output_dir = "dist"
compatibility_date = "2025-06-01"
compatibility_flags = ["nodejs_compat"]
[vars]
PUBLIC_AIRWALLEX_ENV = "demo"
PUBLIC_API_BASE = "https://api.store-b.example.com/api/v1"
RCOMMERCE_API_URL = "https://api.store-b.example.com"
PUBLIC_BRAND_SLUG = "store-b"
PUBLIC_BRAND_NAME = "Store B"
PUBLIC_BRAND_LOGO = "/images/store-b-logo.png"
PUBLIC_BRAND_PRIMARY = "#0F766E"
PUBLIC_HERO_VIDEO_URL = "https://customer-<id>.cloudflarestream.com/<video-id>/iframe?muted=true&preload=true&loop=true&autoplay=true&controls=false"
PUBLIC_BRAND_MENU = "gear:Gear,apparel:Apparel"

Two requirements are easy to miss:

  • compatibility_date = "2025-06-01" — the storefront relies on Pages runtime behavior from this date; do not let the dashboard roll it forward.
  • compatibility_flags = ["nodejs_compat"] — required by the Astro SSR output and the API proxy function.

Set these in the project’s production environment (they apply to the runtime proxy, not the build):

VariableExample
RCOMMERCE_API_URLhttps://api.store-b.example.com
PUBLIC_API_BASEhttps://api.store-b.example.com/api/v1
PUBLIC_AIRWALLEX_ENVdemo or prod
PUBLIC_BRAND_SLUGstore-b
PUBLIC_BRAND_NAMEStore B
PUBLIC_BRAND_LOGO/images/store-b-logo.png
PUBLIC_BRAND_PRIMARY#0F766E
PUBLIC_BRAND_MENUgear:Gear,apparel:Apparel
PUBLIC_HERO_VIDEO_URLCloudflare Stream iframe URL

The Airwallex build-time value is only a fallback: the backend’s checkout summary (airwallex_env) always takes precedence at runtime.

Pages keeps every deployment; roll back from the dashboard (Deployments → ⋮ → Rollback) or redeploy a previous production deployment.