Deploy a UnfoldCMS-Powered Site on Cloudflare Pages
Frontend on Cloudflare's edge, CMS on your host, rebuild on publish.
UnfoldCMS can't run on Cloudflare Pages — it's a PHP app, and Pages serves static assets plus JavaScript Workers. But the architecture developers actually want works great: keep the CMS on a PHP host, deploy your Next.js or Astro front end to Cloudflare's edge, and rebuild automatically when an editor publishes. This guide wires it up, including the Cloudflare deploy hook and edge caching. See also: the Astro integration guide and the Vercel guide.
TL;DR: UnfoldCMS stays on your PHP host. Your front end deploys to Cloudflare Pages and fetches from /api/v1/posts at build time, served from 300+ edge locations. You create a Cloudflare Pages deploy hook, then register it in UnfoldCMS so the post.published webhook fires it. Publish, Pages rebuilds, live worldwide. No GraphQL, no SDK.
How Does the Architecture Work?
The CMS and the front end live in separate places and talk over HTTPS. "Deploy UnfoldCMS on Cloudflare Pages" is the wrong framing — you deploy the site on Pages, with the CMS as its content source.
- UnfoldCMS runs on a PHP host (VPS or shared hosting with PHP 8.3 + MySQL). Editors work here; content lives here.
- Your front end (Next.js / Astro) runs on Cloudflare Pages. Built once, then served from the edge network.
- The link is a Cloudflare Pages deploy hook, triggered by the CMS's signed publish webhook.
What You Need Before Starting
- UnfoldCMS running on a PHP host with at least one published post.
- A front-end project (Astro or Next.js) connected to a Cloudflare Pages project.
- The CMS base URL set as a Pages environment variable, e.g.
CMS_URL=https://cms.yoursite.com. - About 20 minutes.
Step 1: Deploy the Front End to Pages
Connect your repo in the Cloudflare dashboard under Workers & Pages → Create → Pages. Set the build command and output directory for your framework (e.g. astro build produces dist). Add CMS_URL under Settings → Environment variables. The build fetches published content from the public API — no auth for reads:
// runs during the Cloudflare Pages build
const res = await fetch(`${CMS_URL}/api/v1/posts`);
const { data } = (await res.json()).data; // { success, data: { data: [...] } }
After the first deploy you have a static site served from Cloudflare's edge in 300+ cities. Next: make it update on publish.
Step 2: Create a Pages Deploy Hook
A deploy hook is a URL that triggers a production build when POSTed to. In your Pages project under Settings → Builds & deployments → Deploy hooks, create one (name it "CMS publish", branch main). Cloudflare returns a URL like:
https://api.cloudflare.com/client/v4/pages/webhooks/deploy_hooks/xxxxxxxx
Copy it. A POST to this URL rebuilds the site with fresh CMS content.
Step 3: Trigger the Build on Publish
UnfoldCMS ships outgoing HMAC-signed webhooks. On the post.published event it POSTs a signed payload to a URL you register. Connect it to the deploy hook one of two ways:
Option A — direct. Register the Cloudflare deploy hook URL in UnfoldCMS. A POST triggers the build, so it works right away. Trade-off: the CMS signature isn't checked, so anyone with the URL could trigger a build.
Option B — verified Worker (recommended). Put a small Cloudflare Worker in front that verifies the signature, then calls the deploy hook:
// Cloudflare Worker: verify the CMS signature, then trigger the Pages build
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const raw = await req.text();
// UnfoldCMS signs with spatie/laravel-webhook-server — header "Signature",
// value hash_hmac('sha256', rawBody, secret).
const signature = req.headers.get('signature') ?? '';
const key = await crypto.subtle.importKey(
'raw', new TextEncoder().encode(env.CMS_WEBHOOK_SECRET),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['sign'],
);
const mac = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(raw));
const expected = [...new Uint8Array(mac)]
.map((b) => b.toString(16).padStart(2, '0')).join('');
if (signature !== expected) return new Response('Bad signature', { status: 401 });
// Payload: { event, occurred_at, data: { id, title, slug, url, posted_at } }
await fetch(env.PAGES_DEPLOY_HOOK_URL, { method: 'POST' });
return Response.json({ deploying: true });
},
};
Register it in UnfoldCMS. In the admin webhooks settings, add your endpoint (the deploy hook URL for Option A, or your Worker URL for Option B), subscribe to post.published, and for Option B set CMS_WEBHOOK_SECRET on the Worker. Use "Send test" to confirm a build starts.
How Does Edge Caching Fit In?
Because Cloudflare Pages serves static files, your HTML is already cached at the edge after each build — no extra config. The content is regenerated only when the deploy hook fires, so visitors always hit a fast cached page, and the cache refreshes on publish rather than on every request. That's the whole appeal: edge speed with content that still updates when it matters.
| Layer | What it serves | Refreshes when |
|---|---|---|
| Cloudflare edge | Static HTML/assets | Each successful Pages build |
| Pages build | Pulls from CMS API | Deploy hook fires on publish |
| UnfoldCMS host | Source content + API | Editor publishes a post |
Frequently Asked Questions
Can I run UnfoldCMS on Cloudflare Pages or Workers?
No. UnfoldCMS is a PHP/Laravel app needing PHP and MySQL; Pages serves static assets and Workers run JavaScript. Host the CMS on a PHP server and deploy only your front end to Pages, pointing it at the CMS API.
Why use a Worker instead of registering the deploy hook directly?
A Worker lets you verify the CMS's HMAC signature before triggering a build, so random POSTs to a leaked hook URL can't cause deploys. The direct method is simpler but unverified — use the Worker for production.
Is a Cloudflare Pages site faster than running the CMS directly?
For visitors, yes. They hit static HTML cached across Cloudflare's edge, never your origin PHP server. The CMS only does work when an editor publishes and the build runs.
Which CMS events trigger a Pages rebuild?
UnfoldCMS emits the post.published event today. Subscribe to it in the admin and point it at your deploy hook (or Worker) to rebuild whenever a post goes live.
Does UnfoldCMS support GraphQL for the build?
No — it's REST only. The build fetches from endpoints like /api/v1/posts with plain fetch(). There's no GraphQL layer and no SDK to install.
Where to Go From Here
You now have a CMS-powered site on Cloudflare's edge that rebuilds on publish. For the data layer, read the Astro or Next.js integration guide. Prefer Vercel? The Vercel guide follows the same pattern. Full endpoint list is in the API docs.
UnfoldCMS is a one-time license, self-hosted, with the full public API in every tier — see pricing. Keep your content on your own host, serve a blazing front end from Cloudflare, and skip per-seat SaaS bills entirely.
Related: Astro integration · Deploy on Vercel · self-hosted CMS platforms