IT技術ブログ

WordPressからヘッドレスCMS+静的サイトへ移行する手順。記事・画像・URLの移し方、301リダイレクト、フォーム、検索順位を落とさないための確認チェックリスト

WordPressからヘッドレスCMSと静的サイト(Astroなど)へ移行する手順を、記事・画像・URLの移し方、301リダイレクトの置き場所、フォームの作り直し、公開の前後に検索順位を守る確認チェックリストの順に並べました。

この記事の結論:今あるWordPressのサイトをヘッドレスCMSと静的サイトへ移すときは、①旧サイトのURLと画像の一覧を機械で書き出す、②CMSの入力欄を決めて記事を取り込む、③表のサイトを旧URLと同じ形で書き出す、④URLが変わる所だけ301で転送する、⑤フォームを作り直す、⑥一覧と書き出し結果を毎回自動で照合する、⑦公開した日と翌週以降に検索の状況を見る、の7段で進めます。検索順位を守るうえで一番大事なのは、旧URLが「同じURLで開ける」か「301で中身の近いページへ転送される」かのどちらかになっていることを、人の目ではなくスクリプトで全件確かめることです。

この記事は、WordPressのサイトをヘッドレスCMSと静的サイトの構成に移したい中小企業の担当者と、それを請け負う制作者に向けた手順書です。WordPressからヘッドレスCMSへ移行するときの作業を、発注する側が進み具合を確かめられるチェックリストの形にしています。例に使う事実は、やまやまブログ(このブログ)の旧WordPressを、Astroで書き出しCloudflareから配る形へ切り替えたときのものです。ただし、このブログは記事をMarkdownのファイルで持っており、ヘッドレスCMSは使っていません。CMSに取り込む部分は一般的な手順として書き、このブログの実際の作業とは分けて示します。仕様の根拠は末尾にまとめ、2026年10月3日付の版で中身を照らし合わせました。

移行の手順は7段に分け、段ごとに「終わった」と言える条件を決める

WordPressからヘッドレスCMSへの移行は、段ごとに終わりの条件を決めておくと、公開の判断がしやすくなります。全体の流れは次のとおりです。

段 やること 終わったと言える条件
1 旧URL・画像・フォームの棚卸し URLと画像の一覧がファイルとして残っている
2 CMSの入力欄を決めて記事を取り込む 全記事がCMSにあり、件数が旧サイトと一致する
3 表のサイトを旧URLと同じ形で書き出す ビルド結果に、旧URLと同じパスのページがある
4 URLが変わる所に301を置く 変わるURLの全件に、実在する転送先がある
5 フォームを作り直す 送信から、顧客と社内の両方へのメールの到着までを確かめた
6 一覧と書き出し結果の自動照合 照合のスクリプトで、不足が0件
7 公開と、公開後の確認 404の件数が、検索の管理画面で増えていない

以下、段ごとに詳しく書きます。ヘッドレスCMSを入れない移行(記事をファイルで持つ形)でも、2段目以外はそのまま使えます。

1段目:旧サイトのURLと画像は、WordPressのREST APIから書き出して一覧にする

移す対象の一覧は、人が数えるのではなく、WordPress自身のREST APIから取り出して作ります。投稿、固定ページ、カテゴリー、タグ、メディアの5種類を取り出し、各項目の link(ページのURL)と source_url(画像のURL)を集めれば、旧サイトにあるURLの一覧になります。

scripts/export-wp.mjs(取り出しの考え方)js
import fs from 'node:fs/promises';

// WordPress の REST API は1回に最大100件まで。全体のページ数は X-WP-TotalPages の見出しで返る
const BASE = 'https://example.co.jp/wp-json/wp/v2';

async function fetchAll(type) {
  const items = [];
  let total = 1;
  for (let page = 1; page <= total; page++) {
    const res = await fetch(`${BASE}/${type}?per_page=100&page=${page}&_embed`);
    if (!res.ok) throw new Error(`${type} ${page}: ${res.status}`);
    total = Number(res.headers.get('X-WP-TotalPages') ?? 1);
    items.push(...(await res.json()));
  }
  return items;
}

for (const type of ['posts', 'pages', 'categories', 'tags', 'media']) {
  await fs.writeFile(`export/${type}.json`, JSON.stringify(await fetchAll(type)));
}

APIに出てこないURLもあるので、次のものは別に足します。

  • 一覧ページの2ページ目以降のURL。記事の数と1ページの件数から計算する
  • Search Console(ページのインデックス登録のレポート)や、アクセス解析で見られているURL。外からリンクされている古いURLが見つかることがある
  • 旧サイトが出していたサイトマップ。WordPress標準のものか、SEOのプラグインのものかで名前が違う

このブログで書き出した結果は、ページが86件、画像がメディアの一覧とアイキャッチを合わせて290件でした。この2つの一覧が、6段目の照合の正解の表になります。

2段目:CMSの入力欄を先に決め、本文のHTMLを整えてから取り込む

ヘッドレスCMSへの取り込みは、CMS側の入力欄(スキーマ)を決めてから行います。WordPressの記事は「題名・本文・アイキャッチ・カテゴリー・タグ・スラッグ・公開日」に分けて取り出せるので、CMSにも同じ単位の欄を用意し、スラッグの欄は必須にします。

取り込みの前に、本文のHTMLで次のものを片付けます。

本文に残っているもの 起きること 片付け方
ショートコード([gallery] など) 静的サイトでは展開されず、文字のまま表示される 取り込む前にHTMLへ置き換えるか、CMSの欄に分ける
プラグインが出していた目次や装飾 クラス名だけが残り、見た目が崩れる 不要なクラスとタグを外す
本文の中の画像のURL 旧ドメインや旧パスを指したままになる 3段目で決める画像の置き場所に合わせて書き換える
記事どうしのリンク 絶対URLだと、ドメインを変えたときに旧ドメインを指す ルートからの相対パスにそろえる
独自の入力欄(プラグインで足した欄) REST APIに出てこないことがある 出すための設定をするか、データベースから取り出す

取り込みは、CMSの書き込み用のAPIか取り込み機能を使います。書き込み用のAPIキーは取り込みの間だけ作り、終わったら消します。取り込んだら、CMSの件数が1段目の一覧の記事の数と一致するかを確かめます。

3段目:表のサイトは、旧サイトと同じURLの形で書き出す

URLを決めるのはCMSではなく、表のサイトの側です。CMSのスラッグの欄からページのパスを作り、末尾のスラッシュの有無も旧サイトに合わせます。Astroなら、設定でディレクトリの形(/slug/index.html)に書き出し、末尾のスラッシュを付ける側にそろえられます。

画像の置き場所は、次の2つから選びます。

選び方 良い所 注意すること
旧サイトの画像を、WordPressのアップロード用のフォルダと同じパスのまま静的なファイルとして置く 画像のURLが変わらず、転送も要らない 移行後に足す画像とは置き場所が分かれる
画像もCMSに上げ直す 画像の縮小や形式の変換をCMSの機能に任せられる 画像のURLが変わるので、本文の書き換えと、外から貼られた画像への対策が要る

このブログは前者で、旧サイトの画像を同じパスのまま置きました。画像検索や、ほかのサイトに貼られた画像のリンクを切らずに済むからです。ヘッドレスCMSを入れる場合も、旧い画像は前者、移行後の新しい画像はCMS、と分けるのが安全です。

公開の操作をしたときに表のサイトが作り直されるよう、CMSのWebhookと配信先のデプロイフックもここでつないでおきます。

4段目:301リダイレクトは、パスの変更とホスト名の統一で置き場所を分ける

URLが変わる所だけに301(恒久的な転送)を置き、パスの変更とホスト名の統一は別の場所に書きます。Googleは、301と308をどちらも「転送先を正式なURLとして扱う合図」として扱い、302はそうではないとしています。

このブログでの分け方は次のとおりです。

転送の種類 置いた場所 このブログでの例
ホスト名の統一 Pagesのサーバー側の関数(Functions)のミドルウェア apex(wwwの付かないドメイン)と、Pagesが発行する既定のアドレスを、パスとクエリを保ったままwww付きへ301。末尾のスラッシュも同時に足し、転送を1回で済ませる
カテゴリーの名前の変更 Pagesの _redirects(転送の一覧) 名前を変えた分野の旧URLを、変更後の分野の一覧へ301。日本語のURLは、大文字と小文字の%表記を両方書く
旧サイトマップ 同じ設定ファイル WordPress標準の名前とYoastの名前の旧サイトマップを、新しいサイトマップの入口へ301
無くなった記事 同じ設定ファイル 中身の近い記事へ301

旧サイトマップの転送は見落としやすい項目です。検索エンジンは旧サイトの robots.txt に書かれていたサイトマップのURLを覚えているので、そのURLが見つからないページのまま放置されると、新しいページを見つけてもらうのに時間がかかります。

転送を書くときの注意を、公式の資料から拾っておきます。

  • Pagesの転送の設定ファイルは、静的な転送が2,000件、動的な転送(* や :placeholder を使うもの)が100件までで、クエリ文字列での振り分けはできません。件数が多い場合は、関数(Functions)か、ダッシュボードで作る一括の転送のルールに分けます
  • 無くなったページをまとめてトップページへ転送すると、Googleには「見つからないページ」と同じ扱い(ソフト404)にされることがあります。転送先は中身の近いページにします
  • 転送は、Googleの案内では少なくとも1年、できるだけ長く残します

5段目:フォームは静的サイトでは動かないので、作り直して両方の受信まで確かめる

WordPressのフォームのプラグインはWordPressの上で動くため、静的サイトに移すと使えなくなります。お問い合わせや資料請求のフォームは、サーバーレスの関数(Pages Functions など)か、フォームの受付サービスで作り直します。なお、このブログの問い合わせページは会社サイトの窓口へのリンクにしているため、ここは一般的な手順として書きます。

作り直すときの確認項目です。

確かめること 方法
スマホで最後まで入力して送れるか 実機で、最初の欄から送信の完了まで操作する
顧客への自動返信と、社内への通知の両方が届くか 実際に送り、2つのメールボックスで受信を確かめる。迷惑メールのフォルダも見る
送信元のドメインの設定 送信に使うサービスの案内に沿って、SPF・DKIMを設定する
いたずらの送信への対策 人の操作かを確かめる仕組み(Turnstileなど)を入れる
送信の完了を計測できるか 完了のページかイベントで、アクセス解析に記録されるか
旧フォームのURL 旧サイトでフォームがあったURLから、新しいフォームへ301

フォームは問い合わせの入口そのものなので、ここが確かめられるまでは公開しません。

6段目:ビルドのたびに、旧URLと画像の一覧を書き出し結果と照合する

公開の前の確認は、目で見て回るのではなく、1段目の一覧とビルド結果をスクリプトで突き合わせて行います。旧URLごとに「同じパスにページがある」か「301の転送先のページが実在する」かを判定し、どちらでもないものを不足として出します。

このブログではビルドの直後に照合のスクリプトを流し、ページ86件と画像290件の不足が0件になったことを確かめてから切り替えました。転送の行が書いてあっても転送先のページが無いもの、を不合格にしているのがこのスクリプトの要点です。照合の実装と、DNSを2段階で切り替えた経過は、移行作業そのものの記録に書いています。

ヘッドレスCMSを使う場合は、CMSで記事を足したり消したりすると書き出されるページが変わるので、この照合をビルドの手順に組み込み、不足があればビルドを失敗させる形にしておくと、公開後の取りこぼしも防げます。

7段目:公開した日と翌週以降に、検索の状況を確かめる

公開の当日は転送と表示の確認、翌週以降は検索の管理画面でのエラーの確認、と時期で分けて見ます。Googleの案内では、中くらいまでのサイトでも、検索結果のURLが新しいものに置き換わるまで数週間かかることがあります。

公開の当日に確かめること:

  • 旧URLの一覧から代表的なものを curl -I で開き、200か301になること。www なしのURLが www 付きの同じパスへ1回で転送されること
  • 開発中に入れていた noindex や、robots.txt でのクロールの拒否が残っていないこと
  • 新しいサイトマップを送信し、読み込みが成功と表示されるのを見る
  • IndexNowで、BingなどIndexNowに参加している検索エンジンに新しいURLを知らせること(このブログでは、鍵のファイルをサイトに置き、サイトマップに載った全URLをまとめて送れるようにしました)
  • フォームから実際に1件送り、両方のメールが届くこと

翌週以降に確かめること:

  • インデックス登録のレポートで、404や転送のエラーの件数が増えていないか
  • 表示回数とクリック数が、移行の前と比べて大きく落ちていないか
  • CMSで公開した記事が、表のサイトに反映されているか(Webhookとデプロイフックの配線)

移行の進み具合を確かめるチェックリスト

発注する側が制作者に進み具合を聞くときは、次の表の項目ごとに「確かめた方法」を答えてもらうと、抜けが見つかります。

時期 項目 確かめた方法の例
作り始める前 旧URLと画像の一覧がある REST APIと検索の管理画面から作ったファイル
作り始める前 変えないURLと、変えるURLが決まっている URLの対応表
作っている間 CMSの記事の件数が旧サイトと一致する 件数の比較
作っている間 本文にショートコードや旧ドメインの絶対URLが残っていない 文字列の検索
公開の前 旧URLと画像の不足が0件 照合のスクリプトの出力
公開の前 フォームから顧客と社内の両方にメールが届く 実際の送信
公開の前 公開の操作で表のサイトが作り直される テスト用の記事で確かめる
公開の当日 ホスト名の統一と、旧サイトマップの転送 curl -I の結果
公開の当日 サイトマップの送信とIndexNow 送信の結果
公開の翌週以降 404と転送のエラーが増えていない インデックス登録のレポート

株式会社bundlyzeではサイト・HPの制作を行っています。上の表は、このブログを旧WordPressから静的な構成へ切り替えた際に、実際に一つずつ確かめた項目がもとになっています。旧サイトからの移行も含めた作り直しは、ホームページ制作のご案内から相談できます。

手順の裏付けに使った資料(2026年10月3日時点)

内容 出典
URLを変えるサイト移転の進め方と、転送を残す期間 Google 検索セントラル https://developers.google.com/search/docs/crawling-indexing/site-move-with-url-changes
301・308・302の扱いの違い Google 検索セントラル https://developers.google.com/search/docs/crawling-indexing/301-redirects
REST APIの1回あたりの上限と総ページ数の見出し WordPress REST API Handbook https://developer.wordpress.org/rest-api/using-the-rest-api/pagination/
転送の設定ファイルの件数の上限と、使えない書き方 Cloudflare Docs https://developers.cloudflare.com/pages/configuration/redirects/
CMSの公開でビルドを始めるフック Cloudflare Docs https://developers.cloudflare.com/pages/configuration/deploy-hooks/
CMSから配信先への通知の種類 microCMS https://document.microcms.io/manual/webhook-setting
新しいURLの送り方と鍵のファイル IndexNow https://www.indexnow.org/documentation

よくある質問

WordPressの記事のURLは、ヘッドレスCMSに移したあとも同じにできますか?

できます。URLを決めるのはCMSではなく表のサイトの側なので、WordPressのスラッグをCMSの欄に入れておき、表のサイトでそのスラッグからページを書き出せば、同じURLになります。末尾のスラッシュの有無もそろえておきます。

301リダイレクトは、移行したあとどのくらいの期間残しておけばよいですか?

Googleは、転送はできるだけ長く、一般的には少なくとも1年は残すよう案内しています。消す理由が特になければ、そのまま残しておくのが無難です。

WordPressのお問い合わせフォームは、静的サイトでもそのまま使えますか?

WordPressのフォームのプラグインはWordPressの上で動くので、静的なサイトでは使えません。サーバーレスの関数やフォームの受付サービスで作り直し、送信の完了から、顧客と社内の両方にメールが届くまでを実際に送って確かめます。