This document explains how to set up Cloudflare Pages deployment for the Cambridge Beer Festival app at cambeerfestival.app.
📖 For complete CI/CD workflow documentation, see CICD.md
This guide focuses on Cloudflare configuration. For workflow details, triggers, and deployment flows, refer to the CI/CD documentation.
The app uses two separate Cloudflare Pages projects for clean separation between production and staging:
Project 1: cambeerfestival (Production only)
- Production branch:
release - Deploys: Git tags (e.g.,
v2025.12.0) - Custom domain:
cambeerfestival.app
Project 2: cambeerfestival-staging (Staging + PR previews)
- Production branch:
main(serves staging) - Preview branches: PR branches (serve PR previews)
- Deploys: Git main + all PRs
- Custom domain:
staging.cambeerfestival.app
| Git Event | CF Project | CF Branch | URL | Purpose |
|---|---|---|---|---|
| Version tag | cambeerfestival |
release |
cambeerfestival.app |
Production |
Push to main |
cambeerfestival-staging |
main |
staging.cambeerfestival.app |
Staging |
| Pull Request | cambeerfestival-staging |
<branch> |
<branch>.cambeerfestival-staging.pages.dev |
PR previews |
Push to main |
GitHub Pages | N/A | richardthe3rd.github.io/... |
Development |
- Cloudflare account with access to manage Pages and DNS
- GitHub repository with appropriate permissions
- Domain
cambeerfestival.appadded to Cloudflare
You need two separate Cloudflare Pages projects:
cambeerfestival(production)cambeerfestival-staging(staging/previews)
Option A: Let GitHub Actions Create the Projects (Easiest)
Both projects will be automatically created on their first deployment. You can skip this step and jump to step 2 (Get Account ID) and step 3 (Create API Token).
- First push to
mainwill createcambeerfestival-staging - First git tag will create
cambeerfestival
Option B: Create Projects Manually
If you prefer to create the projects manually first:
For Production Project:
- Log in to Cloudflare Dashboard
- Navigate to Workers & Pages
- Click Create application → Pages
- Set Project name:
cambeerfestival - Disable automatic deployments (GitHub Actions will handle deployments)
For Staging Project:
- In Workers & Pages, click Create application → Pages
- Set Project name:
cambeerfestival-staging - Disable automatic deployments
Important: Project names must match the workflow configuration (cambeerfestival and cambeerfestival-staging).
- In Cloudflare Dashboard, click your profile in the top right
- Navigate to Account Home
- Copy your Account ID from the right sidebar
- Save this for GitHub Secrets setup (see below)
If you already have a Cloudflare API token for Workers:
You can reuse the same token by adding Pages permissions to it:
- Go to Cloudflare API Tokens
- Find your existing token (e.g.,
GitHub Actions - Cambridge Beer Festival) - Click Edit (pencil icon)
- Add the following permission:
- Account → Cloudflare Pages → Edit
- Your token should now have:
- Account → Workers Scripts → Edit (existing)
- Account → Cloudflare Pages → Edit (new)
- Click Continue to summary → Update Token
If you don't have an existing token:
- Go to Cloudflare API Tokens
- Click Create Token
- Select Create Custom Token
- Configure the token:
- Token name:
GitHub Actions - Cambridge Beer Festival - Permissions:
- Account → Workers Scripts → Edit (for Workers deployment)
- Account → Cloudflare Pages → Edit (for Pages deployment)
- Zone → DNS → Read (optional, only if you need DNS updates)
- Account Resources: Include → Your Account
- Zone Resources:
- If you added Zone → DNS → Read permission: Include →
cambeerfestival.app - Otherwise: Not needed (Pages and Workers are Account-level resources)
- If you added Zone → DNS → Read permission: Include →
- Token name:
- Click Continue to summary → Create Token
- Copy the token immediately (you won't be able to see it again)
- Save this for GitHub Secrets setup (see below)
Note: Using a single token with both Workers and Pages permissions is simpler and follows the principle of consolidating CI/CD credentials for the same application.
You need to configure one custom domain per project:
- In Cloudflare Dashboard, go to Workers & Pages → Pages
- Select the
cambeerfestivalproject - Go to Settings → Builds & deployments
- Set Production branch to:
release - Go to Custom domains tab
- Click Set up a custom domain
- Enter:
cambeerfestival.app - Click Continue
- Cloudflare will automatically configure the DNS records
- In Cloudflare Dashboard, go to Workers & Pages → Pages
- Create or select the
cambeerfestival-stagingproject - Go to Settings → Builds & deployments
- Set Production branch to:
main - Go to Custom domains tab
- Click Set up a custom domain
- Enter:
staging.cambeerfestival.app - Click Continue
- Cloudflare will automatically configure the DNS records
Note: The cambeerfestival-staging project will be automatically created by GitHub Actions on the first deployment if it doesn't exist.
If you want www.cambeerfestival.app to redirect to the apex domain:
- In the
cambeerfestivalproject, addwww.cambeerfestival.appas a custom domain
DNS Records Created (automatic):
CNAME cambeerfestival.app→cambeerfestival.pages.devCNAME staging.cambeerfestival.app→cambeerfestival-staging.pages.devCNAME www.cambeerfestival.app→cambeerfestival.pages.dev(optional)
The Cloudflare Worker for API proxy has already been updated to allow both custom domains in CORS origins:
https://cambeerfestival.app(production)https://staging.cambeerfestival.app(staging)
When you deploy worker changes:
cd cloudflare-worker
npm ci
cp ../data/festivals.json ./festivals.json
wrangler deployOr let GitHub Actions deploy it automatically on push to main.
- Go to your GitHub repository
- Navigate to Settings → Secrets and variables → Actions
- Verify or add the following secrets:
| Secret Name | Value | Description |
|---|---|---|
CLOUDFLARE_API_TOKEN |
<token from step 3 above> |
API token with Workers + Pages permissions |
CLOUDFLARE_ACCOUNT_ID |
<account ID from step 2 above> |
Your Cloudflare account ID |
GOOGLE_SERVICES_JSON |
<your google-services.json content> |
Firebase config (already exists) |
Note: If you already have CLOUDFLARE_API_TOKEN for Workers deployment, you don't need to change it in GitHub—just ensure you updated the token itself in Cloudflare (step 3) to include Pages permissions. The same token will now work for both Workers and Pages deployments.
Important: Keep these secrets secure. Never commit them to the repository.
The project uses 3 GitHub Actions workflows for CI/CD:
| Workflow | File | Purpose |
|---|---|---|
| Flutter App CI/CD | build-deploy.yml |
App building, testing, and staging deployments |
| Cloudflare Worker | cloudflare-worker.yml |
API proxy and festivals.json deployment |
| Release Web | release-web.yml |
Production releases to cambeerfestival.app |
See CICD.md for complete workflow documentation.
Key features:
- Production releases via version tags (
v*) - Automated testing before deployment
- PR preview deployments to Cloudflare Pages
- Separate worker deployment pipeline
- Staging environment on
main.cambeerfestival.pages.dev
- Go to Settings → Actions → General
- Ensure Actions permissions is set to "Allow all actions"
- Under Workflow permissions, ensure:
- "Read and write permissions" is selected
- "Allow GitHub Actions to create and approve pull requests" is checked (optional)
📖 For complete deployment flows and workflow details, see CICD.md
The app has three main deployment workflows:
Trigger: Creating a version tag
Workflow: .github/workflows/release-web.yml
Steps to deploy:
-
Create and push a tag:
git tag v2025.12.0 git push origin v2025.12.0
-
GitHub Actions automatically:
- Runs tests and analysis
- Builds Flutter web app with
--base-href "/" - Deploys to Cloudflare Pages production
- App is live at
https://cambeerfestival.appwithin 1-2 minutes
Manual trigger:
- Go to Actions tab in GitHub
- Select Release Web to Cloudflare Pages workflow
- Click Run workflow
- Enter version tag (e.g.,
v2025.12.0) - Click Run workflow
Trigger: Push to main or PR when worker/festivals.json changes
Workflow: .github/workflows/cloudflare-worker.yml
Automatic process:
- Edit
cloudflare-worker/**ordata/festivals.json - Commit and push to
main - GitHub Actions automatically:
- Validates festivals.json against schema
- Deploys worker to production
- Worker is live at
https://cbf-data-proxy.richard-alcock.workers.dev
On Pull Requests:
- Validates festivals.json
- Runs
wrangler deploy --dry-runto catch errors
See CICD.md for detailed workflow documentation.
Trigger: Push to main branch or pull requests
Workflow: .github/workflows/build-deploy.yml
This workflow handles all non-production deployments and includes multiple jobs:
Trigger: Push to main branch
Job: deploy-web-preview
Automatic process:
- Push or merge to
main - GitHub Actions automatically:
- Builds web app (reuses artifact from
build-webjob) - Deploys to Cloudflare Pages
mainbranch preview - Creates a stable staging URL (e.g.,
main.cambeerfestival.pages.dev)
- Builds web app (reuses artifact from
Benefits:
- Stable staging environment that mirrors
mainbranch - Test changes before creating production releases
- Production-like environment without affecting live site
Trigger: Opening or updating a pull request
Job: deploy-web-preview
Automatic process:
- Open a pull request to
main - GitHub Actions automatically:
- Builds web app (reuses artifact from
build-webjob) - Deploys to Cloudflare Pages preview environment
- Posts unique preview URL as comment on the PR
- Builds web app (reuses artifact from
- Each PR gets its own unique preview URL
- Preview is automatically updated when you push new commits
Benefits:
- Test changes in production-like environment before merging
- Share preview URLs with team members for review
- No conflicts with staging environment
Trigger: Push to main branch
Job: deploy-web
Automatic process:
- Push or merge to
main - Deploys to
richardthe3rd.github.io/cambridge-beer-festival-app/ - Serves as alternative development/testing environment
After deployment, verify:
- Website loads: Visit
https://cambeerfestival.app - SSL is active: Check for padlock icon in browser
- API calls work: Test loading drink data
- CORS headers: Check browser console for CORS errors (should be none)
- Firebase works: Check analytics and crashlytics
- Service worker: Check for offline functionality
Open browser console on https://cambeerfestival.app and run:
fetch('https://cbf-data-proxy.richard-alcock.workers.dev/festivals.json')
.then(r => r.json())
.then(console.log)Should return festival data without CORS errors.
Check GitHub Actions logs:
- Go to Actions tab
- Click on failed workflow run
- Review logs for errors
Common issues:
- Missing secrets: Add required secrets to GitHub
- Wrong Cloudflare project name: Ensure project is named
cambeerfestival - Invalid API token: Regenerate token with correct permissions
If you see CORS errors in browser console:
- Verify Cloudflare Worker includes
https://cambeerfestival.appinALLOWED_ORIGINS - Redeploy the worker:
cd cloudflare-worker wrangler deploy - Clear browser cache and test again
- Check DNS records in Cloudflare Dashboard → DNS → Records
- Ensure CNAME record exists:
cambeerfestival.app→cambeerfestival.pages.dev - Wait up to 24 hours for DNS propagation
- Check SSL certificate status in Workers & Pages → cambeerfestival → Custom domains
- Verify deployment succeeded in GitHub Actions
- Check Cloudflare Pages deployment status
- Ensure base-href is "/" in build command (not "/cambridge-beer-festival-app/")
- Make changes to code
- Commit and push to
mainbranch - GitHub Actions automatically deploys to both:
- GitHub Pages (staging)
- Cloudflare Pages (production)
- Edit
data/festivals.json - Commit and push to
main - GitHub Actions automatically deploys worker with new data
If deployment breaks production:
- Go to Cloudflare Dashboard → Workers & Pages → cambeerfestival
- Click Deployments tab
- Find the last working deployment
- Click ⋯ → Rollback to this deployment
Or fix and redeploy from GitHub:
- Revert commit:
git revert HEAD - Push to
main:git push origin main - GitHub Actions will deploy the reverted version
- API Token: Keep Cloudflare API token secure. Rotate periodically.
- CORS: Worker only allows specific origins. Don't add wildcards.
- HTTPS: Cloudflare enforces HTTPS. All traffic is encrypted.
- Rate Limiting: Consider enabling Cloudflare rate limiting for API endpoints.
Cloudflare Pages: Free tier includes:
- Unlimited requests
- Unlimited bandwidth
- 500 builds/month
- 1 build at a time
Cloudflare Workers: Free tier includes:
- 100,000 requests/day
- Sufficient for moderate traffic
Both should remain in free tier unless app sees very high traffic.
- CI/CD Workflows: CICD.md - Complete workflow documentation
- Cloudflare Pages Docs: https://developers.cloudflare.com/pages/
- GitHub Actions Docs: https://docs.github.com/en/actions
- Flutter Web Docs: https://docs.flutter.dev/platform-integration/web
Cloudflare Setup:
- Cloudflare Pages project
cambeerfestivalcreated (production) - Cloudflare Pages project
cambeerfestival-stagingcreated (staging/previews) - Production project
cambeerfestival→ Production branch set torelease - Staging project
cambeerfestival-staging→ Production branch set tomain - Custom domain
cambeerfestival.appconfigured oncambeerfestivalproject - Custom domain
staging.cambeerfestival.appconfigured oncambeerfestival-stagingproject - DNS records configured (automatic via Cloudflare)
- Cloudflare Account ID obtained
- Cloudflare API Token updated with both Workers Scripts + Pages permissions
GitHub Setup:
- GitHub Secret
CLOUDFLARE_API_TOKENverified (should work for both Workers and Pages) - GitHub Secret
CLOUDFLARE_ACCOUNT_IDadded - GitHub Secret
GOOGLE_SERVICES_JSONverified - Workflow files committed (
.github/workflows/release-web.ymlandbuild-deploy.yml)
Verification:
- Cloudflare Worker updated with both custom domains in CORS origins
- Push to
maintriggers successful deployment tohttps://staging.cambeerfestival.app - Create tag triggers production deployment to
https://cambeerfestival.app - API calls work without CORS errors on all environments