Cloudflare Pages

The cloudflarePages adapter uses the Direct Upload API. Create the project once in the Cloudflare dashboard (Workers & Pages > Create > Pages > Direct Upload), then:

import { defineConfig } from "@kilim-dev/core";
import { cloudflarePages } from "@kilim-dev/core/adapters";

export default defineConfig({
  url: "https://www.example.com",
  deploy: cloudflarePages({
    accountId: "0123456789abcdef0123456789abcdef",
    project: "my-blog",
    // Pull requests deploy as previews; other runs go to the production branch.
    branch: process.env.GITHUB_HEAD_REF,
  }),
});
  • The API token comes only from the CLOUDFLARE_API_TOKEN environment variable; it needs the Cloudflare Pages: Edit permission. Never put it in the config.
  • branch defaults to the project's production branch. Any other branch is a preview deployment; an empty string counts as not set.
  • message is shown with the deployment in the dashboard (default Deploy <time>).
  • Every deployment includes _kilim/deploy.json, the list of its files and their hashes. The next kilim deploy reads it to show what is added, changed and removed. When a deployment has none (e.g. one made with another tool, or behind Cloudflare Access), Kilim compares by content only and cannot list removed files.
  • _headers and _redirects from the build are sent with the deployment, so CSP headers and redirects work. Files are uploaded only when Cloudflare does not have them yet; files over 25 MiB are rejected.
# .github/workflows/cloudflare.yml
name: Cloudflare Pages
on:
  push:
    branches: [main]
  pull_request:
permissions:
  contents: read
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx kilim deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}