UnfoldCMS + Astro: Headless Integration Guide
Pull content, generate static pages, and rebuild when editors publish.
Astro is built for content sites, which makes it a natural front end for a headless CMS. This guide wires UnfoldCMS to Astro end to end: pulling published posts from the REST API, generating static pages, and rebuilding automatically when an editor publishes — using a signed webhook. Real code, about 25 minutes, and the honest limits. See also: the best CMS for Astro and the Next.js integration guide.
TL;DR: UnfoldCMS runs on your own PHP host. Astro fetches published content from /api/v1/posts (public, no auth) at build time. Because Astro's default output is static, "update on publish" means triggering a rebuild — UnfoldCMS fires an HMAC-signed post.published webhook you point at your host's build hook. No GraphQL, no SDK, just fetch().
How Do UnfoldCMS and Astro Work Together?
UnfoldCMS is the content source; Astro is the site generator. They communicate over a plain REST API. The CMS stays on your server (it's Laravel — PHP and MySQL); the Astro output is static files you can host anywhere.
Three parts:
- Read — Astro calls
GET /api/v1/postsandGET /api/v1/posts/{slug}during the build. - Generate —
getStaticPaths()turns each post into a static HTML page. - Rebuild — On publish, UnfoldCMS POSTs a signed webhook to your host's build hook, which regenerates the site.
What You Need Before Starting
- A running UnfoldCMS install with at least one published post (the public read API ships in Core).
- An Astro 4+ project.
- Your CMS base URL, e.g.
https://cms.yoursite.com. - About 25 minutes.
No API key is needed to read published content — the public endpoints are open and rate-limited to 60 requests per minute.
Step 1: Fetch Content in getStaticPaths
Astro fetches at build time inside getStaticPaths(). UnfoldCMS wraps every response in a { success, data } envelope, so unwrap data once.
// src/lib/cms.ts
const CMS_URL = import.meta.env.CMS_URL; // https://cms.yoursite.com
export async function cms<T>(path: string): Promise<T> {
const res = await fetch(`${CMS_URL}/api/v1${path}`);
if (!res.ok) throw new Error(`CMS ${path} returned ${res.status}`);
const json = await res.json();
return json.data as T;
}
Then build one page per post:
---
// src/pages/blog/[slug].astro
import { cms } from '../../lib/cms';
export async function getStaticPaths() {
const { data: posts } = await cms<{ data: any[] }>('/posts');
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
---
<article>
<h1>{post.title}</h1>
<Fragment set:html={post.body} />
</article>
The body is sanitized HTML (UnfoldCMS runs content through a purifier), so set:html is the intended way to render it.
Step 2: Rebuild on Publish With a Signed Webhook
Astro's default build is static files, so "update on publish" means kicking off a rebuild. UnfoldCMS ships outgoing HMAC-signed webhooks; on the post.published event it POSTs a signed payload to a URL you register. Point that at your host's build hook (Vercel Deploy Hook, Cloudflare Pages deploy hook, Netlify build hook — all just URLs that trigger a build).
If your host's build hook accepts an unauthenticated POST, you can register it directly. To verify the signature yourself first (recommended), put a tiny serverless function in front:
// a serverless relay that verifies the signature, then triggers the build hook
import crypto from 'crypto';
export async function POST(request: Request) {
const raw = await request.text();
// UnfoldCMS signs with spatie/laravel-webhook-server — header "Signature",
// value hash_hmac('sha256', rawBody, secret).
const signature = request.headers.get('signature') ?? '';
const expected = crypto
.createHmac('sha256', process.env.CMS_WEBHOOK_SECRET!)
.update(raw)
.digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!ok) return new Response('Bad signature', { status: 401 });
// Payload: { event, occurred_at, data: { id, title, slug, url, posted_at } }
await fetch(process.env.BUILD_HOOK_URL!, { method: 'POST' });
return Response.json({ triggered: true });
}
Register it in UnfoldCMS. In the admin webhooks settings, add the endpoint URL, subscribe to post.published, and copy the signing secret into CMS_WEBHOOK_SECRET. Use the admin's "Send test" button to confirm the wiring before relying on it.
Static Build vs SSR: Which Astro Mode Fits?
Astro can output static files or run server-side. Pick by how fresh your content must be and whether you want a server at all.
| Mode | Freshness | Needs a server? | Best for |
|---|---|---|---|
| Static + rebuild webhook | Minutes after publish | No | Most content sites — recommended |
| SSR (on-demand) | Always fresh | Yes | Personalized or very high-churn content |
| Static only | Stale until next deploy | No | Docs, rarely-edited sites |
For a blog or marketing site, static plus a rebuild webhook is the sweet spot: free hosting on the edge, and content refreshes within a build cycle of hitting publish.
Frequently Asked Questions
Does UnfoldCMS expose a GraphQL API for Astro?
No. UnfoldCMS is REST-only. You query endpoints like /api/v1/posts with fetch() inside getStaticPaths(). For a static site generator this is simpler than GraphQL — no schema, no extra client.
Is there an Astro integration package or SDK?
No official SDK exists. You use the native fetch() already available in Astro. The small client shown above is all you need.
Can I use Astro Content Collections with UnfoldCMS?
Content Collections expect local Markdown/MDX files, while UnfoldCMS serves content over an API. Fetch in getStaticPaths() instead. You get the same static output, sourced from the CMS rather than the filesystem.
Can UnfoldCMS run on the same host as my Astro site?
UnfoldCMS needs PHP and MySQL, so it runs on a PHP-capable server, not on static-only hosts. Deploy the Astro output to Cloudflare Pages or Vercel and point it at the CMS API.
Which webhook events can trigger a rebuild?
Today UnfoldCMS emits the post.published event. Subscribe to it in the admin and point it at your build hook to rebuild whenever a post goes live.
Where to Go From Here
You now have Astro pulling content from UnfoldCMS, generating static pages, and rebuilding on publish. If you're still choosing a CMS, the CMS for Astro comparison weighs the options honestly. The Next.js guide follows the same pattern with on-demand revalidation instead of full rebuilds. 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. You run your own server, which isn't for everyone, but it means no per-seat SaaS bills and full ownership of your content.
Related: CMS for Astro · Next.js integration · Deploy on Cloudflare Pages