Skip to main content

Cloudflare Pages Deployment

This guide covers deploying the Hyperscape web client to Cloudflare Pages with automatic builds and R2 asset hosting.

Overview

The Cloudflare deployment consists of:
  • Client: Static site hosted on Cloudflare Pages
  • Assets: 3D models, textures, audio hosted on Cloudflare R2
  • CORS: Configured for cross-origin asset loading

Architecture

Prerequisites

Cloudflare Account

  1. Sign up at Cloudflare
  2. Create a Pages project named hyperscape
  3. Create an R2 bucket named hyperscape-assets

GitHub Secrets

Configure in repository settings (Settings → Secrets and variables → Actions):

Cloudflare API Token Permissions

Create a token with these permissions:
  • AccountCloudflare PagesEdit
  • AccountR2Edit

Automatic Deployment

Workflow Trigger

The client deploys automatically on push to main when these paths change:
  • packages/client/**
  • packages/shared/** (contains packet definitions)
  • package.json
  • bun.lockb

Workflow File

.github/workflows/deploy-pages.yml

Build Process

  1. Checkout code with submodules
  2. Setup Bun (latest version)
  3. Install dependencies (bun install --frozen-lockfile)
  4. Build client (bun run build:client):
    • Builds packages/shared first (via Turbo)
    • Builds packages/physx-js-webidl (WASM bindings)
    • Builds packages/client (Vite production build)
  5. Deploy to Pages using Wrangler:
    • Project: hyperscape
    • Branch: main
    • Commit hash and message included

Build Environment Variables

Set during build (in workflow):

Manual Deployment

From Local Machine

From GitHub Actions

  1. Go to ActionsDeploy Client to Cloudflare Pages
  2. Click Run workflow
  3. Select main branch
  4. Choose environment: production or preview
  5. Click Run workflow

R2 Asset Hosting

Bucket Setup

  1. Create R2 bucket:
    • Name: hyperscape-assets
    • Location: Automatic (Cloudflare chooses optimal location)
  2. Configure custom domain:
    • Go to R2 bucket → Settings → Custom Domains
    • Add assets.hyperscape.club
    • Create DNS record as instructed by Cloudflare
  3. Configure CORS:
    Or manually via Wrangler:

Upload Assets

Assets are uploaded separately (not part of client deployment):
This uploads:
  • packages/server/world/assets/ → R2 bucket
  • Preserves directory structure
  • Skips unchanged files (checksum comparison)

Custom Domains

Primary Domain (hyperscape.gg)

  1. In Cloudflare Pages:
    • Go to project → Settings → Custom domains
    • Add hyperscape.gg
    • Add www.hyperscape.gg (optional redirect)
  2. DNS Configuration:
    • Cloudflare will show required DNS records
    • If using Cloudflare DNS, records are added automatically
    • If using external DNS, create CNAME records as shown
  3. SSL/TLS:
    • Cloudflare automatically provisions SSL certificates
    • Wait for certificate status to become “Active”

Asset Domain (assets.hyperscape.club)

  1. In R2 bucket:
    • Go to Settings → Custom Domains
    • Add assets.hyperscape.club
  2. DNS Configuration:
    • Create CNAME record: assets.hyperscape.club<bucket-id>.r2.cloudflarestorage.com
    • Cloudflare will show the exact target

CORS Configuration

Why CORS is Needed

The client (hyperscape.gg) loads assets from a different origin (assets.hyperscape.club), requiring CORS headers.

R2 CORS Configuration

The scripts/configure-r2-cors.sh script configures:
Allowed origins: * (all origins) - safe for public read-only assets Allowed methods: GET, HEAD - read-only access Exposed headers: ETag - for cache validation Max age: 3600 seconds (1 hour) - browser caches CORS preflight

Verify CORS

Environment Variables

Build-Time Variables

Set in .github/workflows/deploy-pages.yml:
These are baked into the build and cannot be changed at runtime.

Runtime Variables

Cloudflare Pages does not support runtime environment variables for static sites. All configuration must be set at build time.

Deployment URLs

Production

Preview

Each commit gets a preview URL:
  • Format: https://<commit-hash>.hyperscape.pages.dev
  • Example: https://50f1a285aa6782ead0066d21616d98a238ea1ae3.hyperscape.pages.dev
Preview deployments use the same build configuration as production.

Troubleshooting

Assets not loading (404 errors)

Symptom: Console errors like Failed to load resource: https://assets.hyperscape.club/models/player/human.glb Causes:
  1. Assets not uploaded to R2
  2. CORS not configured
  3. Custom domain not set up
Fix:

CORS errors

Symptom: Console error Access to fetch at 'https://assets.hyperscape.club/...' from origin 'https://hyperscape.gg' has been blocked by CORS policy Fix:

Build failures

Symptom: GitHub Actions workflow fails during build Common causes:
  1. TypeScript errors
  2. Missing dependencies
  3. Out of memory
Fix:

WebSocket connection failures

Symptom: Client cannot connect to game server Causes:
  1. PUBLIC_WS_URL pointing to wrong server
  2. Railway server not running
  3. CORS/Origin validation failing
Fix:
  1. Verify PUBLIC_WS_URL in workflow matches Railway server
  2. Check Railway deployment status
  3. Verify server allows hyperscape.gg origin (see docs/csrf-cross-origin.md)

Advanced Configuration

Multiple Environments

To deploy to staging/preview environments:
  1. Create separate Pages project: hyperscape-staging
  2. Update workflow to deploy to different project based on branch:

Custom Build Configuration

Edit packages/client/vite.config.ts to customize:
  • Output directory
  • Asset optimization
  • Code splitting
  • Source maps

Caching Strategy

Cloudflare Pages automatically caches:
  • HTML: No cache (always fresh)
  • JS/CSS: Immutable (hashed filenames)
  • Assets: Long cache (1 year)
Configure via packages/client/public/_headers:

Monitoring

Deployment Status

Check deployment status in:
  • GitHub Actions: Actions tab → Deploy Client to Cloudflare Pages
  • Cloudflare Dashboard: Pages → hyperscape → Deployments

Analytics

Cloudflare Pages provides:
  • Web Analytics: Visitor stats, page views, bandwidth
  • Real User Monitoring: Performance metrics, Core Web Vitals
  • Error tracking: JavaScript errors, failed requests
Access via: Cloudflare Dashboard → Pages → hyperscape → Analytics

Logs

View deployment logs:
  • GitHub Actions: Click on workflow run → View logs
  • Cloudflare: Pages → hyperscape → Deployments → View build log