ブログ

ヘッドレスWordPress + Astroでプレビュー機能を実装したときのメモ

WordPressをヘッドレスCMSとして使用しフロントエンドをAstroで構築しているサイトに、プレビュー機能を実装しました。

実装にはいくつかつまずくポイントもありましたが、参考サイトやAIの助けを借りながら、なんとか形にすることができました。本記事はその過程を備忘録としてまとめたものです。

仕様

今回は、以下のような手順で記事の修正から公開までを行えるようにしました。

  1. WordPressの管理画面で、編集したい記事を「下書き保存」する
  2. 記事の内容を編集する
  3. プレビューボタンをクリックして、表示を確認する
  4. ②と③を繰り返しながら問題がなければ、最終的に記事を公開する

実装時の各種バージョンは以下のとおりです。

  • WordPress:6.8.1
  • Astro:v5.5.4
  • PHP: 7.4.33

実装の流れ

今回は、以下のような流れでプレビュー機能を実装しました。

  1. プレビューボタンのクリック時にリダイレクトされるよう設定
  2. 環境変数の設定
  3. プレビュー用テンプレートの作成
  4. 自動デプロイの設定

このうち、①と④はWordPress側の設定、②と③はAstro側での設定となります(一部の項目はホスティングサービス側での対応も必要です)。

プレビューボタンのクリック時にリダイレクトを設定

まずはヘッドレスCMSとして使用しているWordPress側の設定です。

WordPressの投稿編集画面で「プレビュー」ボタンをクリックすると、デフォルトでは以下のようなURLに遷移します。

https://your-domain.com/slug/?preview_id=1234&preview_nonce=a12b3c4d56&preview=true&_thumbnail_id=5678

このままではAstro側でプレビューを表示できないため、フロントエンド側のプレビュー表示に対応するよう、リダイレクトを設定します。WordPressのfunctions.phpに以下のコードを追加します。

functions.php
// プレビューモードでアクセスがあった場合、フロントエンドへリダイレクト
function redirect_post_preview_to_frontend()
{
if (!is_admin() && isset($_GET["preview"]) && $_GET["preview"] == true) {
$id = $_GET["preview_id"] ? $_GET["preview_id"] : $_GET["p"];
$redirect = add_query_arg(
[
"id" => $id,
],
"http://localhost:4321/preview" // リダイレクト先のURL。環境に応じて変更してください。
);
wp_redirect($redirect);
exit;
}
}
add_action('template_redirect', 'redirect_post_preview_to_frontend');

このコードではURLに?preview=trueパラメータが含まれている場合、下書きや未公開記事のプレビューアクセスであると判断し、指定したURLにリダイレクトします。

リダイレクト先のURLには、対象記事のidをクエリパラメータとして付与しています。これにより、フロントエンド側で「どの記事を表示すべきか」を判断できるようになります。

リダイレクト後の遷移先は以下のようなURLになります。

http://localhost:4321/preview?id=123

環境変数の設定

次にAstro側で環境変数の設定を行います。

公開済みの記事であれば、REST API経由でそのままデータを取得できます。 しかし、今回のように「下書き状態」の記事を取得するには、APIリクエスト時に認証が必要です。そのため、認証に必要な情報を環境変数として設定する必要があります。

アプリケーションパスワードの作成

まず、WordPressの管理画面から「ユーザー」一覧を開き、プレビュー権限を持たせるユーザーの編集画面に進みます。必要に応じて、プレビュー専用のユーザーを新たに作成しておくのも良いでしょう。

その中の「アプリケーションパスワード」セクションで、新しいパスワード名を入力し、パスワードを生成します。今回は以下のように設定しました(実際のパスワードとは異なります)。

アプリケーションパスワード名パスワード
astro-previewjn9e GWIC 4HCu MtIW fq2B YHCw
WordPressでのアプリケーションパスワードの設定画面

このままでは環境変数として使えないため、「ユーザー名:アプリケーションパスワード」の形式で結合した文字列を、Base64形式に変換します。

Base64への変換はターミナルなどのコマンドラインで実行できます。WordPressのユーザー名がmasterの場合は以下のコマンドになります。

Terminal window
echo master:jn9e GWIC 4HCu MtIW fq2B YHCw | base64
変換前変換後
master:jn9e GWIC 4HCu MtIW fq2B YHCwbWFzdGVyOmpuOWUgR1dJQyA0SEN1IE10SVcgZnEyQiBZSEN3Cg==

環境変数をAstro側に設定する

プロジェクトのルートディレクトリにある.envファイルに、先ほど生成したBase64の文字列を環境変数として設定します。

WP_AUTH_KEY=bWFzdGVyOmpuOWUgR1dJQyA0SEN1IE10SVcgZnEyQiBZSEN3Cg==
PUBLIC_API_URL=https://your-domain.com/wp-json/wp/v2

WP_AUTH_KEYというキー名を使用していますが、名前は自由に決められます。ただし、この変数はサーバー側専用の非公開情報なので、接頭辞にPUBLIC_をつけないようにします。

また、REST APIのエンドポイントのURLをPUBLIC_API_URLとして環境変数に設定しておくと便利です。

今回はローカル環境でのプレビュー閲覧を前提としていますが、本番環境のテンプレートを使用する場合は、Vercelなどのホスティングサービス側でも環境変数の設定が必要になります。

プレビュー用テンプレートの作成

次にAstro側でプレビュー用のテンプレートを作成します。今回はpreview.astroというファイル名で実装しました。記事の都合上テンプレートのHTMLは簡略化しています。

preview.astro
---
// サーバーレンダリングを有効化
export const prerender = false;
let id;
let currentPostData;
// URLパラメータを取得
try {
if (Astro.request.method === "GET") {
const url = new URL(Astro.request.url);
const params = new URLSearchParams(url.search);
id = params.get("id");
}
} catch (error) {
console.error(error);
}
// ブログIDがない場合は404ページを表示
if (!id) {
return new Response(null, {
status: 404,
statusText: "Not found",
});
}
// 記事の詳細情報を取得
try {
const currentPostRes = await fetch(
`${import.meta.env.PUBLIC_API_URL}/posts/${id}`,
{
headers: {
Authorization: `Basic ${import.meta.env.WP_AUTH_KEY}`,
},
}
);
currentPostData = await currentPostRes.json();
} catch {
return new Response(null, {
status: 401,
statusText: "Invalid Blog ID or Draft Key",
});
}
---
<main>
<h2>{currentPostData.title.rendered}</h2>
<article set:html={currentPostData.content.rendered} />
</main>

ポイントとなる部分を中心に以下にまとめます。

サーバーサイドでページを生成する

テンプレートのフロントマターにexport const prerender = false;を指定することで、ページをサーバーサイドで生成(SSR)するように設定します。

静的ページのままだと、プレビューボタンをクリックしても変更内容が反映されないため、プレビューページではSSRを有効にする必要があります。

SSRを利用するには、ホスティングサービスに応じたアダプターの設定が必要です。Vercelを使用している場合は、公式サイトのAstroサイトをVercelにデプロイするを参考に設定を行うことができます。

記事取得時に認証情報を渡す

記事データの取得時には、ヘッダーに認証情報を追加してWordPress側で認証を通す必要があります。前のセクションで設定した WP_AUTH_KEYをBasic認証形式でヘッダーに渡します。

preview.astro
const currentPostRes = await fetch(
`${import.meta.env.PUBLIC_API_URL}/posts/${id}`,
{
headers: {
Authorization: `Basic ${import.meta.env.WP_AUTH_KEY}`,
},
}
);
currentPostData = await currentPostRes.json();

繰り返しになりますが、この認証ヘッダーがないと「下書き保存」された投稿データを取得できず、正しくプレビューを表示することができません。

以上でプレビューページの実装は完了です。 実際にWordPress上で記事を「下書き保存」し、プレビューボタンをクリックすると、Astroで作成したプレビュー画面が表示されるはずです。

自動デプロイの設定

最後に自動ビルドの設定を行い、記事が公開された段階で自動的にデプロイが実行されるようにします。

VercelでのDeploy Hookの作成

Vercelを使用している場合は、プロジェクトの「Settings」→「Git」→「Deploy Hooks」からDeploy Hookを設定します。Name(任意の名前)と対象のBranchを入力し「Create Hook」をクリックすると、デプロイ用のURLが発行されます。これを控えておきます。

Vercel管理画面でのデプロイフックス設定画面

WordPressにトリガー処理を追加

次にWordPressのfunctions.phpに以下のコードを追加します。

functions.php
// ホスティングサービスのデプロイをトリガーする
function trigger_deploy_on_publish($post_id, $post, $update)
{
$url = 'ホスティングサービスで発行されたDeploy HookのURL';
$context = array(
'http' => array(
'method' => 'POST',
)
);
file_get_contents($url, false, stream_context_create($context));
}
add_action('save_post', 'trigger_deploy_on_publish', 10, 3);

投稿記事が保存されると、Deploy Hookに通知が送られる処理です。これにより記事が公開されると自動的にビルドが実行されるようになります。

ただし、このままの状態では「下書き保存」をしただけでもビルドが実行されてしまう可能性があるため、注意が必要です。 このような不要なビルドを防ぐため、WordPress側の設定を調整して、ビルドの実行をより柔軟にコントロールできるようにします。

以下が調整後のコードです。背景が緑色の部分が追加したコードになります。

functions.php
// 記事が「公開」されたときに、ホスティングサービスのデプロイをトリガーする
function trigger_deploy_on_publish($post_id, $post, $update)
{
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
return;
}
if ($post->post_status !== 'publish') {
return;
}
$url = 'ホスティングサービスで設定したDeploy HookのURL';
$context = array(
'http' => array(
'method' => 'POST',
)
);
file_get_contents($url, false, stream_context_create($context));
}
add_action('save_post', 'trigger_deploy_on_publish', 10, 3);

8〜9行目では、保存時点で投稿ステータスが「公開」の場合のみデプロイが実行されるようにしています。また、4〜6行目では自動保存やリビジョンによる保存処理はスキップするようにしています。

これにより、不要なビルドを防げるようになりました。ステータスが「下書き」のまま保存された場合はビルドされず、「公開」として保存された場合のみビルドが実行されます。

ヘッドレスWordPressへのプレビュー実装時の課題

以上で、ヘッドレスWordPressにおけるプレビュー機能の実装は完了です。実装を進める中で、運用上のいくつかの課題も見えてきました。

リアルタイムプレビューは困難

通常のWordPressサイトでは、「保存」操作を行わなくても「新しいタブでプレビュー」ボタンを使って、リアルタイムにプレビュー画面を表示できます。

一方、ヘッドレスWordPressでは、保存前のデータをAPI経由で取得できないため、リアルタイムプレビューの実現は難しいのが現状です。

ただし、「下書き保存」を行ってからプレビューを確認すれば目的は達成できるため、致命的な問題とは言えないと考えています。

投稿者が複数いるケース

こちらは、リアルタイムプレビュー以上に運用面での課題となる可能性があります。

今回の実装では、特定のユーザー(例:master)のみにプレビューを表示できるように設定しました。しかし、投稿者が複数存在する場合には、それぞれに対応する環境変数を個別に作成・設定する必要があります。

WordPressの管理画面から簡単に設定できる仕組みがないため、新しいユーザーを追加するたびにコードレベルでの調整が必要になります。

また、特定の権限グループに対して一括で認証する仕組みも検討しましたが、現時点では実現が難しい状況です。

もし「ひとつの共通パスワードで複数人にプレビューを許可したい」のであれば、共通の専用ユーザー(例:preview_user)を作って、それを複数人で共有する運用になります。ただし当然ながらセキュリティ面では推奨されません。

参考サイト