This tutorial walks you through building a fast, modern Astro frontend backed by WordPress as a content source. By the end you’ll have:
- An Astro project pulling posts + pages from any WP REST API.
- Per-route caching via Cloudflare KV.
- A clean deploy to Cloudflare Pages.
1. Create the Astro project
Spin up a new Astro project with the minimal starter:
npm create astro@latest mw-headless -- --template minimal --typescript strict
cd mw-headless
npm install
Add the integrations we’ll need:
npm install @astrojs/cloudflare @astrojs/sitemap
Wire them into astro.config.mjs:
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
output: 'server',
adapter: cloudflare(),
integrations: [sitemap()],
site: 'https://your-domain.com',
});
2. Fetch posts from WordPress
WP’s REST API ships out of the box — your posts live at
https://your-wp-site.com/wp-json/wp/v2/posts. Wrap it in a tiny client:
// src/lib/wp.ts
const WP = import.meta.env.WP_REST_URL;
export async function getPosts(perPage = 20) {
const res = await fetch(`${WP}/posts?per_page=${perPage}&_embed=1`);
if (!res.ok) throw new Error(`WP returned ${res.status}`);
return res.json();
}
export async function getPostBySlug(slug: string) {
const res = await fetch(`${WP}/posts?slug=${slug}&_embed=1`);
const arr = await res.json();
return arr[0] ?? null;
}
The _embed=1 flag tells WP to inline the featured image + author so
you don’t make a second round-trip per post.
3. Build the post list page
---
// src/pages/blog/index.astro
import { getPosts } from '../../lib/wp';
const posts = await getPosts();
---
<ul>
{posts.map((p) => (
<li>
<a href={`/blog/${p.slug}`} set:html={p.title.rendered} />
</li>
))}
</ul>
WP returns titles as HTML-encoded strings (“Don’t…”), so use
set:html rather than {p.title.rendered} directly — it’ll decode the
entities. Same goes for content.rendered.
4. Add KV caching
REST calls are 100-300ms from most regions. Wrap the fetch in a KV read-through cache and that drops to under 10ms after the first request:
// src/lib/wp.ts (extended)
export async function getPostsCached(env: any) {
const cached = await env.CACHE.get('posts', 'json');
if (cached) return cached;
const fresh = await getPosts();
await env.CACHE.put('posts', JSON.stringify(fresh), { expirationTtl: 300 });
return fresh;
}
Five-minute TTL is a safe default. If you publish frequently, lower it
or fire a cache-purge webhook from WP on transition_post_status.
5. Deploy
npx wrangler pages deploy dist
Add WP_REST_URL as a Cloudflare Pages environment variable in the
dashboard before your first deploy. CI re-runs every time you push.
Verification
Hit /blog in production. The first request may take 200-400ms (cold
cache + first KV write); subsequent requests should land in ~30ms.
Compare TTFB before/after in Chrome DevTools’ Network panel — that’s
your KV hit/miss bar.
Common errors
| Error | Cause | Fix |
|---|---|---|
WP returned 401 | REST API requires auth (some hosts disable anon access) | Add an Application Password header to the fetch |
Cannot read 'rendered' | Post structure changed (custom REST) | Check _embed=1 is set + handler returns the standard shape |
KV put slow | Worker waiting on KV write | Wrap the put in ctx.waitUntil(...) for fire-and-forget |
What’s next
- Add per-route ISR with Cloudflare’s Cache Rules.
- Wire the
transition_post_statusWP hook to purge KV on publish. - See the Cloudflare KV cache snippet for the generic pattern this builds on.
Get the next one in your inbox. Practical tips, no fluff.
More tutorials
View all- WordPress
How to Prepare Your Plugin or Theme for WordPress 7.0
A step-by-step compatibility process for shipping a WordPress 7.0-ready release — set up a test site, audit your blocks, fix the breaking changes, and ship with confidence.
22 min
- WordPress
WordPress 7.0 for Block Developers: Breaking Changes
WordPress 7.0 enforces the iframed editor, broadens contentOnly mode, and drops PHP 7.3. Here are the breaking changes block and plugin developers must fix before users upgrade.
25 min
- WordPress
WordPress 7.0 AI: Client, Abilities & Connectors
The headline feature of WordPress 7.0 is native AI in Core. Here is what the WP AI Client, the Abilities API, and the Connectors system actually mean for plugin developers.
25 min