zudo-cloudflare-wisdom

Type to search...

to open search from anywhere

Preview Deploys

CreatedApr 4, 2026Takeshi Takatsudo

Per-PR preview deployments on Cloudflare Pages

How Preview Deploys Work

When you deploy with a non-main branch name, Cloudflare Pages creates a preview deployment with a unique URL:

https://<branch>.<account>-<project>.pages.dev

For PR-based previews, use the PR number as the branch:

npx wrangler@4 pages deploy deploy \
  --project-name=my-site \
  --branch="pr-${PR_NUMBER}" \
  --commit-hash="${GITHUB_SHA}" \
  --commit-message="Preview: PR #${PR_NUMBER}"

The preview URL becomes:

https://pr-42.takazudo-my-site.pages.dev

Preview URL Pattern

The URL format is:

https://<branch-slug>.<account-slug>-<project-name>.pages.dev

Where:

  • <branch-slug> is the --branch value (sanitized)
  • <account-slug> is derived from your Cloudflare account
  • <project-name> is the --project-name value

📝 Note

The <account-slug> part is auto-generated by Cloudflare. Check your Pages dashboard to see the exact subdomain format for your account.

Posting Preview URL to PR

Use actions/github-script to post or update a comment with the preview URL:

- name: Comment preview URL on PR
  uses: actions/github-script@v8
  env:
    DEPLOY_URL: ${{ steps.cf-deploy.outputs.deploy_url }}
  with:
    script: |
      const deployUrl = process.env.DEPLOY_URL;
      const prNumber = context.issue.number;
      const marker = '<!-- cf-preview-pr -->';

      const body = [
        marker,
        `## Cloudflare Pages Preview`,
        '',
        `Preview deployment is ready.`,
        '',
        `| | URL |`,
        `|---|---|`,
        `| Preview | ${deployUrl} |`,
        '',
        `Built from commit: \`${context.sha.substring(0, 7)}\``,
      ].join('\n');

      const { data: comments } = await github.rest.issues.listComments({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: prNumber,
      });

      const existing = comments.find(c => c.body.includes(marker));

      if (existing) {
        await github.rest.issues.updateComment({
          owner: context.repo.owner,
          repo: context.repo.repo,
          comment_id: existing.id,
          body,
        });
      } else {
        await github.rest.issues.createComment({
          owner: context.repo.owner,
          repo: context.repo.repo,
          issue_number: prNumber,
          body,
        });
      }

The HTML comment marker (<!-- cf-preview-pr -->) ensures subsequent deploys update the existing comment rather than creating new ones.

Required Permissions

The workflow needs pull-requests: write permission:

permissions:
  contents: read
  pull-requests: write