Headless WordPress
Headless WordPress uses WP only as a content backend, with a separate front end (Next.js, Astro, Nuxt) consuming the REST or GraphQL API. The wins: modern dev experience, faster front end, better security (admin and front end on separate hosts). The trade-offs: previewing, block-aware rendering, and forms all need extra work.
REST/GraphQL API, Next.js front end, preview + caching
EXAMPLE
# 1) Enable a clean public API
# - REST is built in: https://example.com/wp-json/wp/v2/posts
# - GraphQL via WPGraphQL plugin: https://example.com/graphql
# Both expose posts, pages, custom post types, ACF / Meta Box fields.
# 2) Lock down what is exposed
# - In WP, set user accounts to NOT be returned to anonymous (some plugins fix this)
# - Disable XML-RPC and the comments endpoint if unused
# - Cache the API at the edge — Cloudflare / Fastly / nginx fastcgi cache
# 3) Next.js front end — static + ISR
# app/posts/[slug]/page.tsx
import { notFound } from 'next/navigation';
export const revalidate = 300; // ISR every 5 minutes
export async function generateStaticParams() {
const res = await fetch(\`${process.env.WP_BASE}/wp-json/wp/v2/posts?per_page=100&_fields=slug\`,
{ next: { revalidate: 600 } });
const posts: { slug: string }[] = await res.json();
return posts.map(({ slug }) => ({ slug }));
}
export default async function Post({ params }: { params: { slug: string } }) {
const res = await fetch(
\`${process.env.WP_BASE}/wp-json/wp/v2/posts?slug=${params.slug}&_embed\`,
{ next: { revalidate: 300 } });
const [post] = await res.json();
if (!post) notFound();
return (
<article>
<h1 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
<div dangerouslySetInnerHTML={{ __html: post.content.rendered }} />
</article>
);
}
# 4) Preview from inside wp-admin
# Add a 'Preview' button via a small plugin that sends the editor to:
# https://nextjs.example.com/api/preview?secret=${PREVIEW_SECRET}&slug=${post.slug}
# pages/api/preview.ts on Next.js:
# export default async function handler(req, res) {
# if (req.query.secret !== process.env.PREVIEW_SECRET) return res.status(401).end();
# res.setPreviewData({});
# res.redirect('/posts/' + req.query.slug);
# }
# 5) Cache invalidation on publish — webhook from WP to the front end
# In a small plugin or via the 'WP Webhooks' plugin:
# add_action('save_post', function ($post_id) {
# wp_remote_post('https://nextjs.example.com/api/revalidate', [
# 'body' => json_encode([
# 'secret' => 'WEBHOOK_SECRET',
# 'slug' => get_post_field('post_name', $post_id),
# ]),
# 'headers' => ['content-type' => 'application/json'],
# ]);
# });
# /pages/api/revalidate.ts
# export default async function (req, res) {
# if (req.body.secret !== process.env.REVALIDATE_SECRET) return res.status(401).end();
# await res.revalidate('/posts/' + req.body.slug);
# return res.json({ revalidated: true });
# }
# 6) Forms — Gravity Forms / Contact Form 7 still POST to wp-admin/admin-ajax.php
# Headless implication: your front end POSTS to that endpoint, or to a tiny
# server-side proxy that you write.
# 7) Block-aware rendering
# The REST API returns post_content as HTML — fast path: dangerouslySetInnerHTML.
# Block-aware: switch to WPGraphQL + WPGraphQL Content Blocks, then render
# each block as a typed React component.
# 8) Architecture cheat sheet
# - WP at admin.example.com (private), nginx + cache + WAF in front
# - Front end at example.com on a CDN (Cloudflare/Vercel/Netlify)
# - Webhooks from WP -> revalidate on the front
# - One single source of truth for auth (do you actually need WP logins?)
# - Treat WP as a stateful service with backups, not a CMS-and-server combo
# 9) When headless is the WRONG call
# - Site that lives off shortcodes and page builders (Elementor / Divi)
# - Tiny team without a JS front-end skill set
# - Site whose value is the WP plugin ecosystem (WooCommerce hard to headless)
# In those cases, classic WP + caching is usually faster to build and maintain.
Why it matters
Headless WP is the right move when the editorial team genuinely owns the content and the front end is a separate product. It is the wrong move when the value of WP IS the integrated theme + plugin experience — you spend the year rebuilding what WP gave you for free. Be honest about which one you are signing up for.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Use WP as a content API (REST or WPGraphQL); render with Next.js / Astro / Gatsby.Try it Yourself »
Discussion
Loading…