Headless WordPress — WordPress отвечает только за контент (backend), а фронтенд — отдельное приложение на Next.js через API.
Зачем Headless
- Скорость — Next.js генерирует статический HTML
- SEO — SSG/SSR страницы индексируются отлично
- Гибкость — любой дизайн, не ограниченный темами WP
- Безопасность — WordPress не доступен напрямую
Архитектура
WordPress (редактор, БД, медиа) → REST API → Next.js (SSG/ISR страницы).
REST API: базовые запросы
// lib/wordpress.js
const WP_URL = process.env.NEXT_PUBLIC_WP_URL;
export async function getPosts(params = {}) {
const query = new URLSearchParams({ per_page: 10, _embed: true, ...params });
const res = await fetch(`${WP_URL}/wp-json/wp/v2/posts?${query}`, {
next: { revalidate: 60 },
});
return res.json();
}
export async function getPost(slug) {
const res = await fetch(`${WP_URL}/wp-json/wp/v2/posts?slug=${slug}&_embed`, {
next: { revalidate: 3600 },
});
const posts = await res.json();
return posts[0] ?? null;
}
Страница и динамические маршруты
// app/blog/[slug]/page.jsx
export async function generateStaticParams() {
const posts = await getPosts({ per_page: 100 });
return posts.map((post) => ({ slug: post.slug }));
}
export default async function PostPage({ params }) {
const post = await getPost(params.slug);
if (!post) return notFound();
return (
<article>
<h1 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
<div
className="prose"
dangerouslySetInnerHTML={{ __html: post.content.rendered }}
/>
</article>
);
}
On-Demand Revalidation
При публикации в WordPress — автоматически обновляем кеш Next.js:
// functions.php WordPress
add_action('save_post', function($post_id) {
if (wp_is_post_revision($post_id)) return;
$slug = get_post_field('post_name', $post_id);
wp_remote_post('https://nextjs.example.com/api/revalidate', [
'headers' => [
'x-wp-secret' => WP_SECRET,
'Content-Type' => 'application/json'
],
'body' => json_encode(['post_slug' => $slug]),
]);
});
// app/api/revalidate/route.js (Next.js)
export async function POST(request) {
const secret = request.headers.get('x-wp-secret');
if (secret !== process.env.WP_SECRET) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const { post_slug } = await request.json();
await revalidatePath(`/blog/${post_slug}`);
await revalidatePath('/blog');
return Response.json({ revalidated: true });
}
WPGraphQL — альтернатива REST API
Установите плагин WPGraphQL в WordPress и получайте только нужные поля:
export async function getAllPosts() {
const res = await fetch(process.env.WP_GRAPHQL_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `{
posts(first: 100) {
nodes {
id slug title excerpt date
featuredImage { node { sourceUrl altText } }
categories { nodes { name slug } }
}
}
}`,
}),
});
const { data } = await res.json();
return data.posts.nodes;
}
Деплой
- WordPress: VPS / Coolify / любой PHP-хостинг
- Next.js: Vercel, Cloudflare Pages, или свой Node.js сервер
- Связь: WordPress REST API должен быть доступен из Next.js