この記事で分かること
- ヘッドレスWP の仕組みと、なぜ「速い・SEO/AEO に強い」のか
- Astro + WPGraphQL + ACF + Cloudflare Pages の構成と更新フロー
- 実際にこのサイトを構築したときのハマりどころ
WordPress を捨てない理由
「フロントを速くしたい」という話をすると、「じゃあ WordPress をやめましょう」という提案になりがちです。でも、それには問題があります。
WordPress には、長年かけて蓄積されたコンテンツと、担当者が使い慣れた管理画面があります。移行コストは見積もりより必ずかさみます。そして何より、フロントが遅い原因は WordPress そのものではなく、「テーマが全部のページで PHP を動かして HTML を生成している」ことにあります。
ヘッドレス構成では、WordPress はコンテンツの保管場所(CMS)として残します。フロントの HTML は、別のツールがビルド時に一度だけ生成して、CDN に置きます。WordPress の表示エンジンを捨てて、中身だけ使う——これがヘッドレスの考え方です。
このサイトの構成
このサイト(teraone.site)は、以下の構成で動いています。
WordPress(レンタルサーバー)
↓ WPGraphQL で API 提供
Astro(ビルドツール)
↓ ビルド時に GraphQL でコンテンツ取得 → 静的 HTML 生成
Cloudflare Pages(CDN)
→ ブラウザへ配信
構成要素の役割:
| ツール | 役割 |
|---|---|
| WordPress + WPGraphQL | コンテンツの保管・GraphQL API の提供 |
| ACF(Advanced Custom Fields) | カスタム投稿タイプの詳細フィールド管理 |
| Astro | ビルド時にコンテンツ取得 → 静的 HTML を生成 |
| Cloudflare Pages | 静的ファイルを CDN で配信 |
更新フローは一本道
この構成で、更新フローはシンプルになります。
- WordPress の管理画面で記事を書いて「公開」を押す
- WordPress が Deploy Hook(URL)に通知を送る
- Cloudflare Pages がビルドを自動開始する
- Astro が WordPress から最新コンテンツを取得して静的 HTML を生成する
- 1〜2 分後、サイトに新しいコンテンツが反映される
担当者が触るのは WordPress の管理画面だけです。「公開」ボタンを押すとサイトが更新される——これだけ。フロントの仕組みを知らなくても使えます。
なぜ「速い」のか
通常の WordPress テーマは、ページが表示されるたびに PHP が動いて HTML を作ります。データベースへのアクセスが発生し、プラグインが動き、その結果を返します。
ヘッドレス構成では、HTML はビルド時に一度だけ作られて CDN に置かれます。訪問者のリクエストに対して CDN がキャッシュされた HTML を返すだけなので、PHP もデータベースも動きません。これが速い理由です。
このサイトで実測したパフォーマンス(Lighthouse スコア・執筆時点):
| 指標 | スコア |
|---|---|
| パフォーマンス | 98〜100 |
| SEO | 100 |
| アクセシビリティ | 100(全ページ) |
なぜ「AI にも読まれやすい」のか
最近、「AI に読まれるサイト」という話を聞くことが増えました。ChatGPT や Perplexity などの AI アシスタントは、ウェブのページを巡回して情報を集めます。
ここで問題になるのが、主要な AI クローラの多くは JavaScript を実行しないという点です。React や Vue でクライアント側に描画される内容は、AI クローラには見えないことがあります。
Astro + ヘッドレスWP の構成では、コンテンツはビルド時に静的 HTML として出力されます。JavaScript の実行を必要とせず、HTML を読むだけでコンテンツが取れます。これが「AI にも読まれやすい」理由です。
実装のポイント
GraphQL でコンテンツを取得する
Astro では、ビルド時にデータを取得できます。WordPress に WPGraphQL プラグインを入れると、記事や固定ページを GraphQL で取得できるようになります。
// src/lib/wp.ts(抜粋・概念コード)
const ENDPOINT = import.meta.env.WP_GRAPHQL_ENDPOINT;
export async function getPostsList() {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `{
posts(first: 10) {
nodes {
slug
title
date
excerpt
categories { nodes { name } }
}
}
}`,
}),
});
const { data } = await res.json();
return data.posts.nodes;
}
カスタム投稿タイプの詳細フィールドは ACF(Advanced Custom Fields)で管理し、wpgraphql-acf プラグインで GraphQL に公開します。
フォールバックで「絶対に空にならない」設計にする
WordPress への接続が一時的にできない場合に備えて、ダミーデータへのフォールバックを wp.ts に集約しています。営業装置として常にコンテンツを表示させるための設計です。
// wp.ts — フォールバック付きの例(概念コード)
export async function getWorksList() {
try {
const works = await fetchFromWP(); // WordPress から取得
if (works.length > 0) return works;
} catch {
// WP 到達不可
}
return dummyWorks; // ダミーデータで常時表示
}
カスタム投稿タイプの GraphQL 公開には明示指定が必要
カスタム投稿タイプを wpgraphql-acf で GraphQL に公開するとき、2 つの設定が必要です。① CPT 登録時に show_in_graphql と型名を宣言する、② ACF フィールドグループの配列に graphql_types を明示する——この両方が揃わないとフィールドが GraphQL に出てきません。
// CPT登録(mu-plugin・概念コード)
register_post_type('works', [
// ...
'show_in_graphql' => true,
'graphql_single_name' => 'Work', // → GraphQL型名 "Work"
'graphql_plural_name' => 'allWorks',
]);
// ACFフィールドグループをWork型に明示紐付け(概念コード)
acf_add_local_field_group([
'key' => 'group_works_fields',
'fields' => [ /* ... show_in_graphql:1 を各フィールドに */ ],
'location' => [[[ 'param' => 'post_type', 'operator' => '==', 'value' => 'works' ]]],
'show_in_graphql' => 1,
'graphql_field_name' => 'worksFields',
'graphql_types' => ['Work'], // ← CPTはlocation自動マッピングが効かない。これが無いとフィールドが出ない
]);
CPT(カスタム投稿タイプ)は location の自動マッピングが効かないため、graphql_types への明示指定が必須です。フィールドが空で返ってくるときは、まずここを疑ってください。
ドッグフーディング
この記事は、このサイト(teraone.site)の WordPress で書かれていて、ビルド時に Astro が GraphQL で取得して静的 HTML にしています。
つまり、この記事の配信自体が、記事に書いた仕組みの実証になっています。
まとめ
- ヘッドレスWP は「WordPress はそのまま・表示だけ静的化」する構成
- 更新フロー: WP で公開 → Deploy Hook → 自動ビルド → CDN 配信(1〜2 分)
- 速い理由: 訪問時に PHP・DB が動かない。CDN からキャッシュが返るだけ
- AI に読まれやすい理由: 静的 HTML で出力されるので、JS 非実行のクローラにも中身が届く
- 実装のポイント: CPT + ACF の GraphQL 公開は型への明示紐付けが必要
ヘッドレスWP でのサイト構築・リプレイスについて、ご相談はお問い合わせから。→ お問い合わせ