| Filename | Latest commit message | Latest commit date |
|---|---|---|
The caller already knows how to wire secrets.X into with: inputs; the table only needs to name the input, not repeat the secret name in a YAML snippet. |
||
| .. | ||
| cleanup | ||
| deploy | ||
| README.md | ||
cloudflare-pages-preview
Reusable composite actions for deploying PR previews to Cloudflare Pages and cleaning them up when the PR closes.
What it does
-
deploy— Takes a pre-built static HTML directory and:- Injects
X-Robots-Tag: noindexheaders (via_headers) and a<meta name="robots" content="noindex">tag into every HTML page so previews aren't crawled by search engines (optional, on by default) - Deploys to Cloudflare Pages under a per-PR branch alias
(
pr-<number>by default) giving a stable URL across force-pushes - Posts or updates a sticky PR comment with the preview URLs
- Injects
-
cleanup— On PR close:- Deletes all Cloudflare Pages deployments for the PR's branch alias
- Updates the sticky PR comment to indicate cleanup
Prerequisites
- A Cloudflare Pages project must be created in the Cloudflare dashboard
first. Pass its name as
project-name. - Two GitHub Actions secrets (see below).
Required secrets
Create these secrets in the caller repo: GitHub → repo → Settings → Secrets and variables → Actions → New repository secret.
| Repo secret name | Action input | Value |
|---|---|---|
CF_PAGES_PREVIEW_API_TOKEN |
api-token |
Cloudflare API token with Account → Cloudflare Pages → Edit permission, scoped to the single account that owns your Pages project |
CF_PAGES_PREVIEW_ACCOUNT_ID |
account-id |
Cloudflare account ID (hex string from the dashboard sidebar) |
Both deploy and cleanup need the same two secrets.
CF_PAGES_PREVIEW_API_TOKEN
Cloudflare dashboard → My Profile → API Tokens → Create Token → Create Custom Token. Configure:
| Type | Item | Permission |
|---|---|---|
| Account | Cloudflare Pages | Edit |
Under Account Resources, restrict to the single account that owns your Pages project. Do not reuse an account-wide or general-purpose token — a dedicated per-project token limits the blast radius of a leak.
CF_PAGES_PREVIEW_ACCOUNT_ID
Cloudflare dashboard → select any domain, or the Workers & Pages overview → the Account ID is in the right sidebar (copyable hex string).
Not actually secret, but stored as one to keep it co-located with the token.
GitHub token (no setup needed)
The action defaults to ${{ github.token }} (the automatic GITHUB_TOKEN),
which is sufficient for PR commenting with pull-requests: write permission.
Caller workflow requirements
The calling workflow must configure:
| Permissions | contents: read, pull-requests: write |
| Fork-PR guard | if: github.event.pull_request.head.repo.full_name == github.repository — forks do not have access to secrets, so deploys would fail anyway |
| Concurrency | Per-PR group with cancel-in-progress: true to avoid racing deploys |
| Build | Caller is responsible for checkout + building the static site before calling deploy |
Usage
name: Preview
on:
pull_request:
types: [opened, synchronize, reopened, closed]
paths:
- "site/**"
permissions:
contents: read
pull-requests: write
concurrency:
group: preview-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
build-and-deploy:
if: >-
github.event.action != 'closed' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# ... your build steps — produce static HTML in site/public ...
- uses: modelcontextprotocol/actions/cloudflare-pages-preview/deploy@main
with:
directory: site/public
project-name: my-cf-pages-project
api-token: ${{ secrets.CF_PAGES_PREVIEW_API_TOKEN }}
account-id: ${{ secrets.CF_PAGES_PREVIEW_ACCOUNT_ID }}
cleanup:
if: >-
github.event.action == 'closed' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: modelcontextprotocol/actions/cloudflare-pages-preview/cleanup@main
with:
project-name: my-cf-pages-project
api-token: ${{ secrets.CF_PAGES_PREVIEW_API_TOKEN }}
account-id: ${{ secrets.CF_PAGES_PREVIEW_ACCOUNT_ID }}
Note: cleanup does not require actions/checkout — it only calls the
Cloudflare API and GitHub API.
Inputs
deploy
| Input | Required | Default | Description |
|---|---|---|---|
directory |
✅ | — | Path to built static HTML |
project-name |
✅ | — | Cloudflare Pages project name |
api-token |
✅ | — | Cloudflare API token (Pages:Edit) |
account-id |
✅ | — | Cloudflare account ID |
branch |
pr-<PR number> |
CF Pages branch alias | |
inject-noindex |
'true' |
Inject _headers + <meta> noindex |
|
comment-marker |
<!-- cf-pages-preview --> |
Sticky-comment identifier | |
comment-title |
Preview Deployed |
Heading in PR comment | |
github-token |
${{ github.token }} |
Token for PR commenting |
cleanup
| Input | Required | Default | Description |
|---|---|---|---|
project-name |
✅ | — | Cloudflare Pages project name |
api-token |
✅ | — | Cloudflare API token (Pages:Edit) |
account-id |
✅ | — | Cloudflare account ID |
branch |
pr-<PR number> |
CF Pages branch alias to delete | |
comment-marker |
<!-- cf-pages-preview --> |
Sticky-comment identifier | |
github-token |
${{ github.token }} |
Token for PR commenting |
Outputs
deploy
| Output | Description |
|---|---|
deployment-url |
URL of this specific deployment (changes per push) |
alias-url |
Stable branch-alias URL (same across pushes) |