2023年初頭、あるクライアントから連絡をもらった。WordPressで約14,000件の投稿を運営しているメディアパブリッシャーで、6年間かけて4人の開発者によって寄せ集められたテーマを使用していた。その上、Core Web Vitalsスコアは本当に恥ずかしいものだった。モバイルでのLCPは7.2秒を記録していた。WP Rocketを試した、CDNを試した、プラグインの半分さえ削除した。それでも遅い。問題はWordPress自体ではなかった。すべてのページレンダリングがPHP、肥大化したテーマ、2018年以降見直されていないデータベースクエリチェーンを通じて行われていたことが問題だった。Core Web Vitals score that was genuinely embarrassing. Their LCP was clocking in at 7.2 seconds on mobile. They'd tried WP Rocket, they'd tried a CDN, they'd even stripped half their plugins. Still slow. The problem wasn't WordPress itself. It was that every single page render was going through PHP, a bloated theme, and a database query chain that hadn't been looked at since 2018.
重要なポイント:Headless WordPressとAstroの組み合わせはコンテンツサイトの最適解です。編集者向けにはwp-admin、訪問者向けには高速な静的フロントエンド、そしてWPGraphQLが両者を橋渡しします。Headless WordPress with Astro is the content-site sweet spot: wp-admin for editors, static-fast front end for visitors, and WPGraphQL bridging the two.
その瞬間が、私がフロントエンドとしてAstroを使ったheadlessセットアップに本格的にコミットした時だ。流行だからではなく、数千件の投稿と重いエディトリアルコンテンツをプッシュするサイトにとって、それが唯一意味のあるアーキテクチャだったからだ。Astro as the frontend. Not because it's fashionable -- because for a site pushing thousands of posts with heavy editorial content, it was the only architecture that made sense.
正確に設定した方法はこちらです。トレードオフ、実際の設定、私を困らせた部分です。
---
Astro が Next.js ではなく選ばれた理由
この質問はよくもらいます。React中心のチームがすでにいるか、複雑なクライアント側のインタラクティビティが必要なら、Next.jsは明らかな選択肢です。ただ、コンテンツが多いサイトなら?Astroの方が勝ります。そこまで迷う必要もありません。Next.js is the obvious answer if you've already got a React-heavy team or you need complex client-side interactivity. But for content-heavy sites? Astro wins, and it's not particularly close.
AstroはデフォルトではゼロのJavaScriptで配信される。ブログ、ニュースサイト、ドキュメンテーションポータルの場合、それが正しいデフォルトだ。必要な場所でJavaScriptを選択する、むしろ大半は不要な200kbのReactバンドルから外す必要がない。Astroのドキュメントにあるpartial hydrationについて、彼らはそれをIslandsアーキテクチャと呼ぶが、一文で説明するより詳しく説明されている。だが短くいえば、インタラクティブな部分だけがJSを取得する。記事本文、ヘッダー、サイドバー?それらは静的HTML だ。zero JavaScript by default. For a blog, a news site, or a documentation portal, that's the correct default. You opt into JavaScript where you need it, rather than opting out of a 200kb React bundle you mostly don't want. The Astro docs on partial hydration -- they call it the Islands architecture -- explain it better than I can in a sentence, but the short version is: only the interactive bits get JS. The article body, the header, the sidebar? Static HTML.
2022年後半、Next.jsとWordPressで法律コンテンツサイトを構築した。十分に速かったが、クライアントは常に「今では高速になっているはずなのに、なぜモバイルのLighthouseスコアが74なのか」と聞いていた。Hydrationのオーバーヘッド。Astroなら、同じタイプのサイトが今では常に95~98に達する。自慢ではなく、それはそのアーキテクチャが無料で与えるものだ。
Astroが得意でないこと
正直なところ。サイトがリアルタイムのパーソナライゼーション、重いショッピングカート、または多くのコンポーネント間でクライアント側の状態に本当に依存するものが必要なら、Astroは不便に感じられ始めます。Reactアプリではありません。Islands パターンは強力ですが、SPAを構築するのとは異なるメンタルモデルです。2023年中盤にクライアント向けダッシュボードをAstroプロジェクトに無理やり入れようとして、2週間以内にNext.jsに戻しました。何を作ろうとしているのかを知ってください。
---
WordPressをHeadless CMSとして設定する
WordPressは本当に良いheadlessバックエンドです。WP REST APIはコアに含まれていて、ドキュメントも充実していて、編集チームは何も新しく学ぶ必要がありません。その最後のポイントは、開発者が通常認める以上に重要です。WP REST API ships in core, it's well-documented, and your editorial team doesn't have to learn anything new. That last point matters more than developers usually admit.
私が使う設定はこちらです:
- WordPressをサブドメインにインストールする。私はcms.yourdomain.comかapi.yourdomain.comを使用する。基本認証の背後に置くか、最低限、直接的な公開トラフィックを制限する。フロントエンドはyourdomain.com だ。2つの独立したデプロイメント。 -- I use
cms.yourdomain.comorapi.yourdomain.com. Keep it behind basic auth or at minimum restrict direct public traffic. The frontend isyourdomain.com. Two separate deployments. - [WPGraphQL プラグイン](https://www.wpgraphql.com/)をインストールする。コンテンツサイトではRESTより GraphQLを好む。なぜなら、クエリをコンポーネントと一緒に配置でき、必要なフィールドだけを正確にフェッチできるからだ。オーバーフェッチなし。REST APIは問題ないが、投稿タイプあたり15個以上のカスタムフィールドを持つようになると、GraphQLアプローチの方がかなり洗練されている。 -- I prefer GraphQL over REST for content sites because you can co-locate your queries with your components and fetch exactly the fields you need. No over-fetching. The REST API is fine, but once you've got 15+ custom fields per post type, the GraphQL approach is noticeably cleaner.
- Advanced Custom Fields(ACF)と WPGraphQL for ACF 拡張機能をインストールする。このコンボが、WordPressを headless コンテンツモデルとして本当に柔軟にする。投稿タイプごとに構造化されたデータを定義でき、GraphQLを通じて公開でき、Astroはそれを綺麗に消費する。 and the WPGraphQL for ACF extension. This combo is what makes WordPress genuinely flexible as a headless content model -- you can define structured data per post type, expose it through GraphQL, and Astro consumes it cleanly.
- コメント、絵文字、デフォルトのXML-RPCをまだ無効にしていなければ、無効にしてください。これらはオーバーヘッドと攻撃対象を増やすだけです。 if you haven't already. These add overhead and attack surface you don't need.
- ビルドを始める前に、パーマリンクを適切に設定してください。Astroのルートが既に設定されている途中でパーマリンクを変更するのは、本当に面倒です。 to something sensible before you start building. Changing them mid-project when your Astro routes are already set is a genuine pain.
人々を困らせる一つのこと:CORS。デフォルトでは、WordPressはAstro開発サーバー(localhost:4321で実行中)からのリクエストを許可しません。開発中は、テーマの functions.php またはちょっとしたユーティリティプラグインに以下を追加してください:localhost:4321) make requests to your WP install. Drop this into your theme's functions.php or a small utility plugin during development:
`` add_action('init', function() { header("Access-Control-Allow-Origin: *"); }); `` add_action('init', function() { header("Access-Control-Allow-Origin: *"); }); ``
本番環境では特定のオリジンに限定してください。当然のことながら。
---
Astroプロジェクト構造
複数プロジェクト全体で、これは一貫性のある自分のやり方です。ダース単位のヘッドレスビルドをしてきて、これが機能するものです:
`` src/ components/ layouts/ pages/ index.astro blog/ [slug].astro lib/ wpgraphql.ts ← すべての WP クエリロジックはここに格納 styles/ `` src/ components/ layouts/ pages/ index.astro blog/ [slug].astro lib/ wpgraphql.ts ← all WP query logic lives here styles/ ``
lib/wpgraphql.ts ファイルは、すべてのGraphQLフェッチを一元化する場所だ。ページファイル全体に散らばったインラインのフェッチ呼び出しはない。すべてのクエリは名前付きで、エクスポートされた非同期関数だ。14,000件の投稿でこれをデバッグする際、午前2時に何かが壊れたら、後であなた自身に感謝するだろう。lib/wpgraphql.ts file is where I centralise every GraphQL fetch. No inline fetch calls scattered across page files. Every query is a named, exported async function. Debugging this across 14,000 posts when something breaks at 2am -- you'll thank yourself later.
ビルド時に投稿をフェッチする
Astro の getStaticPaths はここで最も重要です。数千の投稿を持つブログの場合:getStaticPaths is your bread and butter here. For a blog with thousands of posts:
`` export async function getStaticPaths() { const posts = await getAllPostSlugs(); // calls WPGraphQL return posts.map(post => ({ params: { slug: post.slug }, })); } `` export async function getStaticPaths() { const posts = await getAllPostSlugs(); // calls WPGraphQL return posts.map(post => ({ params: { slug: post.slug }, })); } ``
getAllPostSlugs は after カーソルを使ってWPGraphQLを通じてページネーションする。WordPressのGraphQLレイヤーはデフォルトでリクエストあたり100件の投稿を返すため、14,000件の投稿では140件のリクエストをビルド時に行う。恐ろしく聞こえる。実際には、まともなサーバーでは、フルビルドは約4~5分で実行される。1日に何度か再構築するサイトにとっては完全に許容可能だ。 paginates through WPGraphQL using after cursors -- WordPress's GraphQL layer returns 100 posts per request by default, so for 14,000 posts you're making 140 requests at build time. That sounds scary. In practice, on a decent server, the full build runs in about 4-5 minutes. Perfectly acceptable for a site that rebuilds a few times per day.
---
画像処理で気が狂わないようにする
これは誰もあまり話題にしない部分だ。WordPressは画像URLをCMSのサブドメインを指すように保存する。Astroが静的ビルドを実行するとき、その画像はまだcms.yourdomain.comに存在する。つまり訪問者のブラウザはWordPressサーバーから画像をフェッチしており、潜在的にCDNをバイパスしている可能性がある。cms.yourdomain.com -- which means your visitors' browsers are fetching images from your WordPress server, potentially bypassing your CDN.
これを処理するいくつかの方法:
- 両方のドメインの前に Cloudflare を配置する。最もシンプルなオプションだ。yourdomain.com と cms.yourdomain.com の両方を Cloudflare 経由でプロキシし、/wp-content/uploads/* に積極的なキャッシング設定を施せば、ほぼ問題ない。 Simplest option. Proxy both
yourdomain.comandcms.yourdomain.comthrough Cloudflare, configure aggressive caching on/wp-content/uploads/*, and you're mostly fine. - メディア移行プラグインを使う。私はWP Offload Mediaが好きだ。S3(または互換性のあるストレージ)にアップロードを移動し、URLを自動的に書き換える。これは本格的なトラフィックが予想されるサイトで使用するアプローチだ。WordPressサーバーは画像の配信を完全に停止する。 I like WP Offload Media -- it moves uploads to S3 (or compatible storage) and rewrites URLs automatically. This is the approach I use for any site expecting serious traffic. Your WordPress server stops serving images entirely.
- AstroのImageコンポーネント。ビルド時に制御できる画像(GraphQLでフェッチされたアイキャッチ画像など)の場合、リモートURLをAstroの<Image>コンポーネントに渡すと、最適化、リサイズ、ビルド出力からの配信が行われる。素晴らしく機能する。ただし投稿本文のHTMLに埋め込まれた画像には機能しない。それには別の処理が必要だ。 For images you control at build time (featured images pulled via GraphQL), you can pass the remote URL into Astro's
<Image>component and it'll optimise, resize, and serve them from your build output. Works brilliantly. Does not work for images embedded in post body HTML -- that requires a different pass.
Seahawk Mediaは去年、トラベルコンテンツクライアントがいた。約8,000の投稿で、画像が非常に多く、記事あたり平均12枚の画像。ヘッドレスセットアップでさえ、WordPressサーバーは単に画像リクエストで酷使されていた。S3 + CloudFrontへの移行により、オリジンの帯域幅が94%削減された。ホスティング料金にとって本当に革新的だった。
---
インクリメンタルビルドと再構築の問題
スタティック生成を大規模で運用する際の実際の課題がこれだ:編集者が午後 3 時に投稿の訂正を公開して、フルリビルドが完了するまで 5 分待たなければいけない。ニュースルーム環境ではこれは受け入れ難い。
私が使ってきた方法がいくつかある:
オプション1:NetlifyまたはVercelをオンデマンドISR付きで使う。Astroはアダプタでサーバーサイドレンダリングをサポートしている。ハイブリッドモードでAstroを実行でき、ほとんどのページは静的だが特定のルートはオンデマンドでレンダリングされる。ニュースサイトの場合、直近30日間の投稿を静的に事前レンダリングし(高トラフィック、速度が必要)、古いアーカイブページはサーバーレンダリングでオンデマンド実行する。両方の長所がある。 Astro supports server-side rendering with adapters -- you can run Astro in hybrid mode where most pages are static but specific routes are rendered on-demand. For a news site, I'll often statically pre-render the last 30 days of posts (high traffic, needs speed) and set older archive pages to server-render on demand. Best of both worlds.
オプション2:Webhook トリガー部分ビルド。WordPressは投稿保存時にWebhookを発火させる(WP Webhooksプラグインなら簡単)。そのWebhookがNetlifyまたはVercelのデプロイフックをヒットする。ビルドが実行され、変更があった部分のみをフェッチする。本当の部分ビルドではない。Astroはすべてを再ビルドするが、ビルドを高速に保つと4分は実用的だ。 WordPress fires a webhook on post save (easy with the WP Webhooks plugin). That webhook hits a Netlify or Vercel deploy hook. The build runs, it fetches only what's changed. It's not truly partial -- Astro still rebuilds everything -- but if you keep your build fast, 4 minutes is workable.
オプション3:単にSSRをすべてに使う。NodeアダプタでAstroをVPS(私はこれにHetznerを使う。安い、高速、信頼性がある)にデプロイする。すべてのページはリクエスト時にレンダリングされ、NginxまたはCloudflareレベルで積極的にキャッシュし、投稿の更新は即座だ。これは50,000を超える投稿を持つ本格的なパブリッシング運用で実行する方法だ。 Deploy Astro with the Node adapter to a VPS (I use Hetzner for this -- cheap, fast, reliable). Every page renders on request, you cache aggressively at the Nginx or Cloudflare level, and you have instant post updates. This is what I'd do for a proper publishing operation over 50,000 posts.
本音を言えば、ほとんどのサイトはオプション1または3の複雑性を必要としません。Webhook でトリガーされる4分間のリビルドはコンテンツサイトの90%に十分です。
---
パフォーマンス:実際に得られるもの
冒頭のパブリッシャープロジェクト。Astro移行後に何が起きたかというと:
- モバイル上の LCP は7.2秒から1.1秒に低下しました(WebPageTest でロンドンノードからテストした結果)1.1s on mobile (tested with WebPageTest from a London node)
- Total Blocking Time は約800ミリ秒からゼロに低下しました(デフォルトではゼロJS のことを思い出してください)0ms (zero JS by default, remember)
- Google Search Console の Core Web Vitals レポートはデプロイメントから6週間以内に「良好」URL が3%から91%に向上しました91% "Good" within six weeks of deployment
- ホスティングコストは削減されました。WordPress サーバーはページを提供しなくなり、API レスポンスのみになったためです
これは魔法ではありません。PHP レンダリングをクリティカルパスから除去し、すべての読者に400kb のテーマ JavaScript バンドルを配信するのをやめることで起こるだけです。
---
つまずきやすいポイント
本音を言うと。実際のプロジェクトでデバッグしなくてはいけなかったこと:
- 下書き投稿のプレビュー。ヘッドレス設定では本当に面倒です。WordPressのネイティブプレビューはフロントエンドレンダリングに依存しています。WordPressのプレビューnonce を受け取ってWPGraphQL経由で下書きを取得するカスタムプレビューエンドポイントをAstroで構築する必要があります。難しくはありませんが、きちんとやるのに1日かかります。 This is genuinely annoying in a headless setup. WordPress's native preview relies on front-end rendering. You need to build a custom preview endpoint in Astro that accepts a WordPress preview nonce and fetches the draft via WPGraphQL. Not hard, but it takes a day to do properly.
- リダイレクト。古いサイトに.htaccessに数百個のリダイレクトがあった場合、それらはいまWordPressサーバーに存在しています。Astroの設定で複製するか、WordPressにアクセスしたままで特定パスをプロキシするか、どちらかが必要です。両方やったことがあります。Astroで複製するほうが長期的にはきれいです。 If the old site had hundreds of redirects in
.htaccess, those live on the WordPress server now. You need to either replicate them in Astro's config, or keep WordPress accessible and proxy specific paths. I've done both. Replicating in Astro is cleaner long-term. - 検索。WordPressの組み込み検索はヘッドレス設定では役に立ちません。Algoliaと「WP Search with Algolia」プラグインを使っています。WPで投稿をインデックス化して、Astroの Island コンポーネントからAlgoliaをクエリします。よく機能します。 WordPress's built-in search is useless in a headless setup. I use Algolia with the WP Search with Algolia plugin. Index your posts in WP, query Algolia from an Astro Island component. Works well.
- メニューとナビゲーション。WordPressのメニューはWPGraphQLを通じて公開するときに奇妙にしぶとい。wpgraphql-acfルートが往々にしてより簡潔になる。ナビゲーションをACF repeaterとしてモデル化して完了。 WordPress menus are weirdly fiddly to expose through WPGraphQL. The
wpgraphql-acfroute often ends up being cleaner -- just model your nav as an ACF repeater and call it done.
---
FAQ
WPGraphQLが必要ですか、それともREST APIだけでいいですか?
REST APIは絶対に使えます。WordPressコアに組み込まれていて、追加プラグインは不要です。標準的な投稿タイプと最小限のカスタムフィールドを持つシンプルなサイトなら問題ありません。GraphQLの出番は、複数のカスタムフィールドを持つ複雑なコンテンツモデルがある場合です。必要なフィールドだけを1回のリクエストで取得でき、_embedパラメータやネストされたREST呼び出しと格闘する必要がありません。それぞれのクエリで時間を節約できます。どちらを選ぶかはあなた次第です。個人的には、一定の複雑さを超えるとGraphQLの方がクリーンに感じます。_embed parameters and nested REST calls, saves time on every query you write. Up to you. I just find GraphQL cleaner past a certain complexity threshold.
メンバー限定コンテンツのWordPress認証はどのように処理しますか?
JWT認証が標準的なアプローチです。JWT Authentication for WP REST APIプラグインをインストールして、ログイン時にトークンを発行し、GraphQLリクエストヘッダーに含めます。Astro側では、ユーザー固有のコンテンツがリクエストごとにサーバー側で取得されるようSSRルート(静的ではなく)で処理します。これを静的に行おうとしてはいけません。そんなことをすれば悪夢です。JWT Authentication for WP REST API plugin, issue tokens on login, pass them in your GraphQL request headers. On the Astro side, you'd handle this with an SSR route (not static) so the user-specific content is fetched server-side per request. Don't try to do this statically -- that way lies madness.
小規模ブログにとっては過度ではありませんか?
おそらくそうです。500件以下のポストと1人のエディターであれば、ヘッドレスセットアップを維持するオーバーヘッドは見合いません。優れたWordPressテーマを使い、画像を最適化して、それで済ませてください。このアーキテクチャが報われるのは、ボリューム、編集の複雑さ、またはパフォーマンスが収益に実質的な影響を与えるレベルのトラフィックがある場合です。
本番環境のホスティング構成はどのような感じですか?
WordPress(CMSのみ)は小規模VPSまたはマネージドWordPressホスト上で実行します。私はKinstaまたはCloudwaysを使用しています。Astroフロントエンドはプロジェクトに応じてVercel、Netlify、またはNginx付きHetzner VPSにデプロイします。すべての前面にCloudflareを配置します。中規模なコンテンツサイトの月間総コストは通常£60~£120で、読み込みに苦労していたオールインワンWordPressホストでクライアントが支払っていた金額より安いことがほとんどです。less than what clients were paying for an all-in-one WordPress host that was struggling under the load.
---
正直な要約は以下の通りです。ヘッドレスWordPressとAstroは、コンテンツサイトにしばらくの間起きた最良の事柄の1つです。新しいからではなく、ツールがようやくそのアイデアに追いついたからです。WPGraphQLは安定しており、Astroのビルドシステムは高速で、パフォーマンスゲインは実数で測定可能です。
アーキテクチャ、特に画像戦略とリビルドアプローチを早い段階で正しく設計すれば、後で火消しに費やす時間が大幅に減ります。本当にそれだけです。
