Skip to main content

Preview Deployments & Branch Control

Learning Focus

By the end of this module you will understand how Cloudflare Pages preview deployments work, how to configure branch-specific behaviors, and how to use them as a staging environment for your Docusaurus documentation.

How Preview Deployments Work

When you push to any branch other than your production branch, Cloudflare Pages creates a preview deployment — a fully live, isolated copy of your site with a unique URL.

flowchart LR
MAIN["main branch\n(production)"] -->|push| PROD["docs.yourdomain.com\nproduction deployment"]
FEAT["feature/new-section\n(preview)"] -->|push| PREV["abc123.my-docs.pages.dev\npreview deployment"]
STAGING["staging branch\n(preview)"] -->|push| STAG["staging.my-docs.pages.dev\nalias preview"]

style PROD fill:#16a34a,color:#fff,stroke:#15803d
style PREV fill:#f6821f,color:#fff,stroke:#e5711e
style STAG fill:#7c3aed,color:#fff,stroke:#6d28d9

Preview URL Formats

URL typeFormatExample
Commit hash<hash>.project.pages.deva1b2c3d.my-docs.pages.dev
Branch alias<branch-slug>.project.pages.devfeature-new-api.my-docs.pages.dev
Project defaultproject.pages.devmy-docs.pages.dev (production only)

Branch names are slugified for the URL:

  • feature/new-sectionfeature-new-section
  • docs/update-v2docs-update-v2

Branch Deploy Controls

Configure Which Branches Deploy

In Cloudflare Pages dashboard:

  1. Go to your project → SettingsBuilds & Deployments
  2. Scroll to Branch deploy controls

Options:

SettingBehavior
All branchesEvery branch push creates a preview (default)
NoneOnly production branch deploys
Custom listSpecify allowed branch patterns

Branch Pattern Examples

# Exact match
main
staging
develop

# Wildcard patterns
feature/* → matches feature/new-section, feature/api-docs
docs/* → matches docs/update, docs/v2
release/v* → matches release/v1.0, release/v2.1

Configure via API

Set branch deploy controls via API
curl -X PATCH \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"deployment_configs": {
"preview": {
"deployments_enabled": true
}
}
}' | jq .

Preview-Specific Environment Variables

Preview deployments can have different environment variables than production:

Set preview-specific variables
# Production: point to real API
wrangler pages secret put API_URL \
--project-name my-docs --env production
# Value: https://api.yourdomain.com

# Preview: point to staging API
wrangler pages secret put API_URL \
--project-name my-docs --env preview
# Value: https://staging-api.yourdomain.com

Detect Environment in Docusaurus

docusaurus.config.js — environment detection
const isProd = process.env.CF_PAGES_BRANCH === 'main';

const config = {
url: isProd
? 'https://docs.yourdomain.com'
: `https://${process.env.CF_PAGES_URL}` || 'https://my-docs.pages.dev',

// Disable some plugins in preview to speed up builds
plugins: [
isProd && './plugins/expensive-plugin',
].filter(Boolean),
};

Built-in Cloudflare Pages Environment Variables

These are automatically set during every build:

VariableValueExample
CF_PAGES1 (always)1
CF_PAGES_COMMIT_SHAFull commit hashabc123def456...
CF_PAGES_BRANCHCurrent branch namemain, feature/nav
CF_PAGES_URLPreview URL for this buildhttps://abc123.my-docs.pages.dev

Using a staging Branch as Staging Environment

A common pattern for Docusaurus documentation teams:

Staging branch workflow
# Create a staging branch
git checkout -b staging
git push origin staging

# Now staging branch gets:
# - Its own preview deployment: https://staging.my-docs.pages.dev
# - Its own environment variables (staging API keys, etc.)
# - Protected from accidental production deploys

# Workflow:
# 1. Work on feature/xxx branches
git checkout -b feature/new-api-guide
# ... make edits ...

# 2. Merge to staging for review
git checkout staging
git merge feature/new-api-guide
git push origin staging
# → https://staging.my-docs.pages.dev updated automatically

# 3. After review, merge to main
git checkout main
git merge staging
git push origin main
# → docs.yourdomain.com updated

Access Control for Preview Deployments

By default, preview deployments are publicly accessible. To restrict access:

Cloudflare Access (Zero Trust)

Protect preview deployments with Cloudflare Access:

  1. Go to Zero TrustAccessApplications
  2. Click Add an applicationSelf-hosted
  3. Set the application domain to *.my-docs.pages.dev
  4. Configure a policy (e.g., email allowlist, OTP)
  5. Only authenticated users can view preview URLs
Free Tier Limit

Cloudflare Access is free for up to 50 users. For larger teams, upgrade to the Zero Trust paid plan.

Pages Access Header Protection (Quick Method)

Add a simple secret header check in Pages Functions:

functions/_middleware.ts — preview guard
export const onRequest: PagesFunction = async (context) => {
const isProd = context.env.CF_PAGES_BRANCH === 'main';

// Allow production through
if (isProd) {
return context.next();
}

// Require preview access header
const accessKey = context.request.headers.get('X-Preview-Key');
if (accessKey !== context.env.PREVIEW_ACCESS_KEY) {
return new Response('Unauthorized', { status: 401 });
}

return context.next();
};

GitHub Pull Request Integration

When using the GitHub integration, Cloudflare Pages automatically:

  1. Comments on the PR with the preview URL
  2. Sets a GitHub deployment status check on the PR
  3. Updates the status when the build succeeds or fails

This means in your GitHub PR you see:

✅ Cloudflare Pages — Preview ready
Visit Preview: https://abc123.my-docs.pages.dev

Disabling PR Comments

Disable PR comments via API
curl -X PATCH \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"source": {
"config": {
"pr_comments_enabled": false
}
}
}' | jq .

Managing Preview Deployments via Wrangler

Preview deployment management
# List all deployments (includes previews)
wrangler pages deployment list --project-name my-docs

# Filter to see only preview deployments
wrangler pages deployment list \
--project-name my-docs | grep -v "main"

# Deploy directly to a named branch (creates a preview)
wrangler pages deploy ./build \
--project-name my-docs \
--branch feature/new-guide

# This creates a preview at:
# https://feature-new-guide.my-docs.pages.dev

Skipping Deployments

To skip a build for a specific commit, add [skip ci] or [ci skip] to your commit message:

Skip deployment for a commit
git commit -m "docs: fix typo [skip ci]"
git push origin main
# → No build triggered

This is useful for:

  • Commits that only change non-content files (.gitignore, README.md)
  • Bulk commits during content reorganization
  • Administrative commits

Preview Deployment Limits

ResourceFree Tier
Preview deploymentsUnlimited
Branch deploymentsUnlimited
Preview deployment retentionActive until project is deleted
Aliases per deployment1 (branch alias)

Key Takeaways

  • Every non-production push creates a preview deployment with a unique URL.
  • Preview URLs follow the pattern <branch-slug>.project.pages.dev.
  • Preview deployments can have different environment variables than production.
  • Use a staging branch as a formal staging environment for team review workflows.
  • Built-in variables like CF_PAGES_BRANCH and CF_PAGES_URL let your build behave differently per environment.
  • Protect previews with Cloudflare Access or a custom middleware function.

What's Next