How to rebuild a Cloudflare site on a schedule
By Flavio Copes
Learn how to rebuild a Cloudflare Pages site on a schedule using a deploy hook, triggered either by a GitHub Action cron or a Cloudflare Worker cron.
A static site on Cloudflare Pages rebuilds when you push to Git. Most of the time that’s exactly what you want.
But sometimes you want it to rebuild on its own, on a schedule, with no push at all.
I hit this with scheduled blog posts. I write a post, give it a future date, and the site hides it until that day. But “until that day” only works if the site rebuilds on that day. Nobody’s pushing at 7am to make a post go live.
The same need comes up for a site that shows data which goes stale: prices, a changelog, anything pulled in at build time.
Here’s how I solved it, and the trade-off I had to pick between.
The key piece: a deploy hook
Cloudflare Pages has a feature called a deploy hook. It’s a secret URL. POST to it, and Cloudflare runs a fresh build and deploy, exactly as if you’d pushed.
You create one in the dashboard: open your Pages project, then Settings → Builds & deployments → Deploy hooks. Give it a name, pick the branch to build (for me, master), and you get a URL.
Test it from your terminal:
curl -X POST "https://api.cloudflare.com/client/v4/pages/webhooks/deploy_hooks/your-hook-id"
Run that, check your dashboard, and you’ll see a new build start. That’s the whole trick.
Now you just need something to press that button once a day. There are two good ways, and this is the real decision.
Option 1: a GitHub Action
If your code already lives on GitHub, the simplest trigger is a scheduled workflow that curls the hook.
Create .github/workflows/scheduled-rebuild.yml:
name: Daily rebuild
on:
schedule:
- cron: "15 5 * * *"
workflow_dispatch: {}
jobs:
rebuild:
runs-on: ubuntu-latest
steps:
- run: curl -fsS -X POST "${{ secrets.CF_PAGES_DEPLOY_HOOK }}"
Put the hook URL in a GitHub secret called CF_PAGES_DEPLOY_HOOK (repo Settings → Secrets and variables → Actions), so it’s not in your code.
That workflow_dispatch line lets you also trigger it by hand from the GitHub UI, which is handy for testing.
The good:
- It’s one small file, right in your repo.
- Nothing extra to deploy or maintain.
- You can run it manually whenever.
The not-so-good:
- GitHub’s scheduled runs are best-effort. They can be late by 5 to 15 minutes, sometimes more when GitHub is busy.
- A scheduled workflow pauses after 60 days of no activity in the repo. Not a problem for an active project, but worth knowing.
- It ties your rebuild to GitHub.
Option 2: a Cloudflare Worker cron
The other way keeps everything on Cloudflare. You make a tiny Worker that runs on a schedule and fetches the hook.
The Worker is almost nothing:
export default {
async scheduled(event, env, ctx) {
await fetch(env.DEPLOY_HOOK, { method: 'POST' })
},
}
Its wrangler.jsonc sets the schedule:
{
"name": "daily-rebuild",
"main": "src/index.ts",
"compatibility_date": "2026-06-20",
"triggers": {
"crons": ["15 5 * * *"]
}
}
Store the hook URL as a secret and deploy:
npx wrangler secret put DEPLOY_HOOK
npx wrangler deploy
The good:
- It’s all on Cloudflare, no GitHub dependency.
- The cron timing is solid.
The not-so-good:
- It’s a separate project to set up, deploy, and keep around.
- That’s a fair amount of ceremony for what is, in the end, a one-line
fetch.
Which one I’d pick
Here’s the thing people get wrong: a Worker cron is not “simpler.” Both options hit the same deploy hook. The Worker can’t build your site itself, since Cloudflare’s build system is what runs your build. So all the Worker does is POST the hook, same as the Action.
The difference is just the wrapper around that POST. The GitHub Action is one file in a repo you already have. The Worker is a whole separate deployable.
So my rule of thumb:
- Code already on GitHub? Use the Action. Less to manage.
- Want zero GitHub dependency, or everything in one place on Cloudflare? Use the Worker cron.
Both work. Pick based on where you want the moving part to live.
One gotcha: time zones
Whichever you choose, the cron runs in UTC. My posts go live at 07:00 my time, which is 05:00 UTC, so I schedule the rebuild at 15 5 * * *, a little after, to be safe.
If you set a schedule and the rebuild seems to happen at the wrong hour, this is almost always why.
That’s the whole pattern
A deploy hook turns “rebuild my site” into a single POST. A scheduler presses it on a timer. With that, a static site on Cloudflare can publish scheduled content, refresh stale data, or redeploy nightly, all without you touching anything.
The deploy hook details are in the Pages docs, and the Worker schedule side is in the Cron Triggers docs.
Related posts about cloudflare: