Why Migrate to Headless WordPress
Traditional WordPress renders pages server-side through PHP themes. While this works well for simple sites, it creates performance bottlenecks at scale: every page request executes PHP, queries the database, and renders templates. Plugin bloat compounds the problem — each active plugin hooks into the request lifecycle.
Headless WordPress decouples the CMS from the frontend. WordPress becomes a content management API, and a modern frontend framework handles rendering. The result is dramatically faster page loads, better security (the WordPress admin is not publicly accessible), and a superior developer experience.
This playbook covers the complete migration path, based on headless WordPress projects where we handle WordPress performance optimization and infrastructure.
Pre-Migration Assessment
Before migrating, audit your current WordPress installation:
Content Inventory
SELECT post_type, COUNT(*) as count
FROM wp_posts
WHERE post_status = 'publish'
GROUP BY post_type
ORDER BY count DESC;
Document every content type, custom field, taxonomy, and relationship. Custom post types and ACF (Advanced Custom Fields) groups need explicit API exposure.
Plugin Dependency Analysis
Categorize plugins into three groups:
- Content plugins (ACF, WPML, Yoast): These affect content structure and need API-side equivalents
- Frontend plugins (Elementor, WPBakery): These become unnecessary in headless mode
- Backend plugins (WooCommerce, Gravity Forms): These need API endpoints or webhook integration
Setting Up the WordPress API
Exposing Custom Fields
// functions.php
add_action('rest_api_init', function () {
register_rest_field('post', 'acf_fields', [
'get_callback' => function ($post) {
return get_fields($post['id']) ?: [];
},
'schema' => [
'description' => 'ACF fields for this post',
'type' => 'object',
],
]);
});
Custom Endpoints for Performance
The default WP REST API returns more data than frontends typically need. Create lean custom endpoints:
add_action('rest_api_init', function () {
register_rest_route('headless/v1', '/posts', [
'methods' => 'GET',
'callback' => function (WP_REST_Request $request) {
$page = $request->get_param('page') ?: 1;
$per_page = $request->get_param('per_page') ?: 12;
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => $per_page,
'paged' => $page,
'post_status' => 'publish',
]);
$posts = array_map(function ($post) {
return [
'id' => $post->ID,
'slug' => $post->post_name,
'title' => $post->post_title,
'excerpt' => wp_trim_words($post->post_content, 30),
'date' => $post->post_date,
'image' => get_the_post_thumbnail_url($post->ID, 'large'),
'categories' => wp_get_post_categories($post->ID, ['fields' => 'names']),
];
}, $query->posts);
return new WP_REST_Response([
'posts' => $posts,
'total' => $query->found_posts,
'pages' => $query->max_num_pages,
]);
},
'permission_callback' => '__return_true',
]);
});
Building the Next.js Frontend
Data Fetching Layer
// lib/wordpress.ts
const WP_API_URL = process.env.WORDPRESS_API_URL!;
export async function getPosts(page = 1, perPage = 12) {
const res = await fetch(
`${WP_API_URL}/headless/v1/posts?page=${page}&per_page=${perPage}`,
{ next: { revalidate: 60 } }
);
if (!res.ok) throw new Error("Failed to fetch posts");
return res.json();
}
export async function getPost(slug: string) {
const res = await fetch(
`${WP_API_URL}/headless/v1/posts/${slug}`,
{ next: { revalidate: 300 } }
);
if (!res.ok) throw new Error("Post not found");
return res.json();
}
Incremental Static Regeneration
Use ISR to serve statically generated pages that revalidate in the background:
// app/blog/[slug]/page.tsx
export const revalidate = 300;
export async function generateStaticParams() {
const { posts } = await getPosts(1, 100);
return posts.map((post: { slug: string }) => ({ slug: post.slug }));
}
Content Preview System
Editors need to preview unpublished content. Implement a preview mode:
// WordPress: Generate preview links for the headless frontend
add_filter('preview_post_link', function ($link, $post) {
$frontend_url = 'https://example.com';
$token = wp_create_nonce('wp_rest');
return "{$frontend_url}/api/preview?id={$post->ID}&token={$token}";
}, 10, 2);
Infrastructure Setup
The headless architecture requires separate hosting for the WordPress API and the Next.js frontend:
WordPress Backend: Host on a managed server with PHP-FPM, MariaDB, and Redis object cache. Restrict public access to the REST API endpoints only — block wp-admin access to internal IPs.
Next.js Frontend: Deploy on Vercel or a Kubernetes cluster with edge caching. The frontend makes API calls to the WordPress backend during build and on-demand revalidation.
CDN Layer: CloudFront or Cloudflare in front of both services, with aggressive caching for static assets and API responses.
Migration Checklist
- Export content inventory and validate all custom fields are API-accessible
- Set up API authentication with application passwords or JWT
- Build and test all frontend pages against the API
- Configure webhook-based revalidation for content updates
- Implement 301 redirects for changed URL structures
- Run parallel sites for two weeks before DNS cutover
- Disable the WordPress frontend theme after cutover
Performance Results
Typical improvements after headless migration:
- TTFB: 800ms to under 100ms
- LCP: 3.5s to under 1.2s
- Server costs: 40 percent reduction (WordPress no longer renders pages)
- Build time: under 3 minutes for 1,000+ pages with ISR
The migration effort pays for itself within months through better Core Web Vitals, higher search rankings, and reduced hosting costs.
Need help with this?
Our team handles this kind of work daily. Let us take care of your infrastructure.
Related Articles
WordPress 6.8 "Cecil": What's New & How It Makes Your Site Better
A deep dive into WordPress 6.8 Cecil, covering speculative prefetch, bcrypt password hashing, BLAKE2b token encryption, editor improvements, classic theme style variations, and new developer APIs.
WordPressEnhancing WordPress Security
A comprehensive guide to securing WordPress sites, covering updates, strong passwords, hosting, two-factor authentication, security plugins, SSL, login limits, wp-config hardening, and backups.
WordPressHow to Optimize Your WordPress Site SEO with WP Rocket
A step-by-step guide to configuring WP Rocket for optimal SEO performance, covering caching, file optimization, media loading, database cleanup, CDN integration, and add-ons.