Preview Deployments & Branch Control
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 type | Format | Example |
|---|---|---|
| Commit hash | <hash>.project.pages.dev | a1b2c3d.my-docs.pages.dev |
| Branch alias | <branch-slug>.project.pages.dev | feature-new-api.my-docs.pages.dev |
| Project default | project.pages.dev | my-docs.pages.dev (production only) |
Branch names are slugified for the URL:
feature/new-section→feature-new-sectiondocs/update-v2→docs-update-v2
Branch Deploy Controls
Configure Which Branches Deploy
In Cloudflare Pages dashboard:
- Go to your project → Settings → Builds & Deployments
- Scroll to Branch deploy controls
Options:
| Setting | Behavior |
|---|---|
| All branches | Every branch push creates a preview (default) |
| None | Only production branch deploys |
| Custom list | Specify 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
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:
# 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
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:
| Variable | Value | Example |
|---|---|---|
CF_PAGES | 1 (always) | 1 |
CF_PAGES_COMMIT_SHA | Full commit hash | abc123def456... |
CF_PAGES_BRANCH | Current branch name | main, feature/nav |
CF_PAGES_URL | Preview URL for this build | https://abc123.my-docs.pages.dev |
Using a staging Branch as Staging Environment
A common pattern for Docusaurus documentation teams:
# 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:
- Go to Zero Trust → Access → Applications
- Click Add an application → Self-hosted
- Set the application domain to
*.my-docs.pages.dev - Configure a policy (e.g., email allowlist, OTP)
- Only authenticated users can view preview URLs
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:
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:
- Comments on the PR with the preview URL
- Sets a GitHub deployment status check on the PR
- 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
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
# 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:
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
| Resource | Free Tier |
|---|---|
| Preview deployments | Unlimited |
| Branch deployments | Unlimited |
| Preview deployment retention | Active until project is deleted |
| Aliases per deployment | 1 (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
stagingbranch as a formal staging environment for team review workflows. - Built-in variables like
CF_PAGES_BRANCHandCF_PAGES_URLlet your build behave differently per environment. - Protect previews with Cloudflare Access or a custom middleware function.
What's Next
- Continue to Custom Domains & SSL to configure your production domain on Cloudflare Pages.