Headless WordPress is a setup where WordPress remains only a content store and an editing panel, while a separate front-end application in React, Vue or Next.js renders pages for visitors. WordPress serves data through an API, and the front end requests it and draws it with its own templates. Here is when this setup pays off and how to build the pairing without losing speed.
When headless makes sense, and when a regular theme does
A headless setup makes sense when a project already has a front-end team, needs one shared codebase for a website and a mobile app, or must show content in several channels at once — on the site, in the app, in a partner's widget. If the task is a plain brochure site or a blog, a classic WordPress theme with caching is cheaper and more reliable.
The downside of headless is double infrastructure: you must maintain both WordPress and a separate front-end server or static hosting for the build.
How WordPress serves data to the front end
The main channel is the built-in WordPress REST API, which returns posts and pages at addresses like /wp-json/wp/v2/posts. For complex queries with nested relations (a post, its author, categories and media in one request), many projects install the WPGraphQL plugin — it adds a /graphql endpoint with flexible queries instead of a dozen separate REST calls.
curl -s https://site.example/wp-json/wp/v2/posts?per_page=5&_embed=1
The _embed=1 parameter loads the author, categories and featured image in one response — without it, the front end would need a separate request for every related entity.
An example of fetching data in a front-end app
On the Next.js side, a request to the REST API looks like a plain fetch during the page build step.
export async function getStaticProps() {
const res = await fetch('https://site.example/wp-json/wp/v2/posts?_embed=1');
const posts = await res.json();
return { props: { posts }, revalidate: 60 };
}
The revalidate: 60 option turns on incremental regeneration: the page is rebuilt no more than once every 60 seconds, instead of on every visitor request.
How to refresh the front end after publishing a post
Waiting for a scheduled rebuild is not always convenient — an editor expects the material to appear right after pressing "Publish". The fix is a webhook: the WordPress publish_post hook pings a rebuild URL on the front-end side.
<?php
add_action('publish_post', function ($post_id) {
wp_remote_post('https://frontend.example/api/revalidate', array(
'body' => array('secret' => 'REPLACE_WITH_TOKEN', 'post_id' => $post_id),
'timeout' => 5,
));
});
The secret token in the request body is needed so outsiders cannot trigger a rebuild at will and create extra load.
Headless versus classic WordPress
| Parameter | Headless | Classic theme |
|---|---|---|
| Front-end speed | high with a static build | depends on cache and hosting |
| Entry barrier | needs a front-end developer | a front-end layout person is enough |
| SEO out of the box | requires manual meta tag setup | handled by Yoast SEO and similar |
| Draft preview | needs a separate preview mode | works right away |
Common problems with the pairing
CORS errors in the browser console mean the REST API does not allow requests from the front end's domain — add the Access-Control-Allow-Origin header through the rest_pre_serve_request filter. Slow API responses on a site with many posts are usually fixed by an object cache — details are in the article about Redis cache for WordPress. Automating cache checks and purges on deploy is convenient with a script built on WP-CLI.
Checklist before launching a headless project
- The API pairing is defined: plain REST API or WPGraphQL added for complex queries.
- A front-end rebuild webhook is set up for publishing and updating posts.
- A draft preview mode is added for editors before publishing.
- CORS headers are configured for the front end's domain.
- An object cache on the WordPress side reduces load from frequent API requests.