Skip to content
PageAudit

Audit Cloudflare Pages deploys with PageAudit

A GitHub Actions workflow deploys the site with Wrangler and audits the live page. The job fails when the page has an SEO error.

Updated

1Add the Cloudflare secrets

In the repository settings, add the Actions secrets CLOUDFLARE_API_TOKEN (a token with Cloudflare Pages: Edit) and CLOUDFLARE_ACCOUNT_ID.

2Add the workflow

Save the file as .github/workflows/deploy.yml. Change example-site to your Pages project and dist to your build folder. Wrangler comes from your devDependencies.

name: Deploy and audit
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      PAGE_URL: https://example-site.pages.dev/
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        with:
          node-version: 24
      - run: npm ci && npm run build
      - name: Deploy to Cloudflare Pages
        run: npx wrangler pages deploy dist --project-name=example-site
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
      - name: Audit the live page with PageAudit
        run: |
          status=$(curl -sS -o report.json -w '%{http_code}' -X POST https://pageaudit.online/api/audit \
            -H 'content-type: application/json' -d "{\"url\": \"$PAGE_URL\"}")
          STATUS=$status python3 - <<'PY'
          import json, os, sys
          status = os.environ["STATUS"]
          report = json.load(open("report.json"))
          if status in ("402", "429"):
              print("::warning::No free PageAudit audits left today.")
              sys.exit(0)
          if status != "200":
              print(f"::error::PageAudit answered {status}: {report.get('error')}")
              sys.exit(1)
          counts = report["counts"]
          print(f"Score {report['score']}: {counts['errors']} errors, {counts['warnings']} warnings")
          for issue in report["issues"]:
              if issue["severity"] == "error":
                  print(f"::error::{issue['code']}: {issue['message']}")
          sys.exit(1 if counts["errors"] else 0)
          PY

3Push and read the audit

Push to main. The last step audits the live page. The log shows the score, and each SEO error comes as an annotation.