Skip to content
Back to skills

Github Actions Cloudflare Pages

ASecurity

Set up the andykenward/github-actions-cloudflare-pages GitHub Action to deploy a site to Cloudflare Pages — production deploys from the default branch, preview deploys with a pull request comment, fork-safe workflow_run deploys, and cleanup with its delete action. Use when adding, fixing or reviewing a GitHub Actions workflow that deploys to Cloudflare Pages with this action.

  • 35 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 21, 2026
devopsnodeexpressgitapi

Works with

  • api

Security analysis

A100/100

Scanned October 6, 2026

npx -y skills add andykenward/github-actions-cloudflare-pages --skill github-actions-cloudflare-pages --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Github Actions Cloudflare Pages?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Github Actions Cloudflare Pages
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/andykenward-github-actions-cloudflare-pages-github-actions-cloudflare-page/badge)](https://www.skillsdirectory.com/skills/andykenward-github-actions-cloudflare-pages-github-actions-cloudflare-page)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: github-actions-cloudflare-pages
description: Set up the andykenward/github-actions-cloudflare-pages GitHub Action to deploy a site to Cloudflare Pages — production deploys from the default branch, preview deploys with a pull request comment, fork-safe workflow_run deploys, and cleanup with its delete action. Use when adding, fixing or reviewing a GitHub Actions workflow that deploys to Cloudflare Pages with this action.
---

# Deploy to Cloudflare Pages with andykenward/github-actions-cloudflare-pages

Full docs: [README](https://github.com/andykenward/github-actions-cloudflare-pages#readme) · [delete action](https://github.com/andykenward/github-actions-cloudflare-pages/blob/main/delete/README.md) · [action.yml](https://github.com/andykenward/github-actions-cloudflare-pages/blob/main/action.yml)

## 1. Learn the project

- The build command and its output directory → `directory`. If Pages Functions live in a subfolder (`functions/` not at the repo root), that folder → `working-directory`, and `directory` is relative to it.
- The default branch → the production branch.
- Whether pull requests come from forks. Forks need the `workflow_run` variant (step 3).
- The Cloudflare account ID and Pages project name — ask the user if they aren't in the repo.

## 2. Do the steps outside the workflow file

These can't be expressed in YAML. Run them, or ask the user to — they need admin access to the repository:

```sh
# Only if there is no Pages project yet.
npx wrangler pages project create <project-name> --production-branch <default-branch>

# The token needs the "Cloudflare Pages: Edit" permission. gh prompts for it —
# never ask the user to paste the token into the conversation.
gh secret set CLOUDFLARE_API_TOKEN
gh variable set CLOUDFLARE_ACCOUNT_ID --body "<account-id>"
gh variable set CLOUDFLARE_PROJECT_NAME --body "<project-name>"

# The action can't create GitHub Environments, and a missing one fails the run.
gh api --method PUT "repos/{owner}/{repo}/environments/production"
gh api --method PUT "repos/{owner}/{repo}/environments/preview"
```

## 3. Write the workflow

Save as `.github/workflows/cloudflare-pages.yml`. Replace `main` with the default branch (in `on:` and in `github-environment`), add the build steps, and set `directory`:

```yaml
name: Cloudflare Pages Deploy
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}

permissions: {}

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 15 # The action waits up to 10 minutes for Cloudflare.
    permissions:
      actions: read # Only required for a private repo.
      contents: read
      deployments: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      # Install dependencies and build the site here, e.g. `npm ci && npm run build`.
      - name: Deploy to Cloudflare Pages
        uses: andykenward/github-actions-cloudflare-pages@6ef4e36d90efd24526eb81a38ae463c1169905ef #v3.6.0
        with:
          cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          cloudflare-account-id: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
          cloudflare-project-name: ${{ vars.CLOUDFLARE_PROJECT_NAME }}
          directory: dist
          github-token: ${{ secrets.GITHUB_TOKEN }}
          github-environment: ${{ (github.ref == 'refs/heads/main' && 'production') || 'preview' }}
```

**Pull requests from forks**: fork runs get no secrets, so deploy from a second workflow triggered by `workflow_run`. Follow the README's [fork example](https://github.com/andykenward/github-actions-cloudflare-pages#fork-pull-requests-with-workflow_run) and [custom branch name](https://github.com/andykenward/github-actions-cloudflare-pages#custom-branch-name): save the PR number as an artifact in the `pull_request` workflow, then pass it to `pr-number` and `branch: pr-<number>`.

**Cleanup**: to delete a pull request's preview deployments when it closes, add a second workflow:

```yaml
name: Cloudflare Pages Delete
on:
  pull_request:
    types: [closed]
    branches: [main]

permissions: {}

jobs:
  delete:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    permissions:
      actions: read # Only required for a private repo.
      contents: read
      deployments: write
      pull-requests: write
    steps:
      - uses: andykenward/github-actions-cloudflare-pages/delete@6ef4e36d90efd24526eb81a38ae463c1169905ef #v3.6.0
        with:
          cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
```

## Rules

- Keep `uses:` pinned to a full commit SHA with its `#vX.Y.Z` comment. To upgrade, find the latest release with `gh release view --repo andykenward/github-actions-cloudflare-pages --json tagName`, then its commit with `gh api repos/andykenward/github-actions-cloudflare-pages/commits/<tag> --jq .sha`.
- Trigger only on `push`, `pull_request`, `workflow_dispatch` or `workflow_run` — any other event fails the step.
- Never build `pr-number` or `branch` from `github.event.workflow_run.pull_requests[0]`: it is empty for pull requests from forks.
- For `workflow_dispatch` and `workflow_run`, the step fails when no pull request matches. Set `pr-number`, or use `push` if a comment isn't wanted.
- Use only `github-environment` names that exist, and keep `timeout-minutes` at 15 or more.
- Cloudflare decides production vs preview from the branch, not from `github-environment` — the project's production branch must be the one mapped to `production`.

## Verify

After the first run (`gh run watch`), check:

- the step's `url` output and the job summary;
- the pull request comment, for pull request runs;
- a new deployment in the repository's Environments (`gh api "repos/{owner}/{repo}/deployments" --jq '.[0]'`).

## Troubleshooting

A failed, canceled or skipped Cloudflare build fails the step with `Create Deployment: the Cloudflare Pages build failed. Build log: <url>` — open that link for Cloudflare's build output. The full list of messages is in the [README](https://github.com/andykenward/github-actions-cloudflare-pages#troubleshooting).

| Log message                                                                | Fix                                                                                               |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `Input required and not supplied: <input>`                                 | Set the input. On a fork's `pull_request` run secrets are empty — use the `workflow_run` variant. |
| `GitHub Environment: Not created for <name>`                               | Create the environment (step 2), or fix the `github-environment` expression.                      |
| `GitHub Action event name '<name>' not supported.`                         | Trigger on `push`, `pull_request`, `workflow_dispatch` or `workflow_run`.                         |
| `No pull request found in workflow_run event matching head branch and sha` | A fork or branch with no pull request — pass `pr-number` (see the fork example).                  |
| `No pull request node id found for workflow_dispatch event`                | Dispatch from a branch with an open pull request, or set `pr-number`.                             |
| `… waiting for the deploy stage to complete`                               | Cloudflare didn't finish within 10 minutes — check the build in the Cloudflare dashboard.         |
| `Wrangler: timed out after 30m`                                            | The upload ran for 30 minutes — check `directory` isn't far larger than intended.                 |
| The job is cancelled at its time limit                                     | Raise `timeout-minutes` to at least 15.                                                           |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.