WordPress を Astroの静的サイトへ移行

長らくさくらインターネットの VPS で運用してきた WordPress を、Astro + Cloudflare Pages の静的サイトに移行しました。動機は固定費の削減、サーバ運用の手間、そして WordPress のセキュリティ対策です。2026年6月に切り替えを終えたので、移行の手順と、想定どおりにいかなかった点を残しておきます。

なぜWordPressをやめたのか

理由は3つあります。

  • 固定費の削減。VPS は毎月1,000円程度で、金額だけ見れば大きくありません。ただ最近は AI のサブスクなど他の費用が地味に積み上がってきていて、ほとんど手を入れていないブログのためにサーバ代を払い続けるのが痛くなってきました。静的サイトなら Cloudflare Pages の無料枠で収まります。
  • VPS 運用の手間。OS やミドルウェアの面倒を見る時間が取れなくなってきました。
  • WordPress のセキュリティ対策。本体・プラグインの更新を怠ると管理画面とデータベースが攻撃対象になり続けます。静的サイトにすれば、そもそも攻撃される側の仕組みが残りません。

移行先を Astro にしたのは、個人サイトの技術選定では「新しく学べる技術を1つ混ぜる」ことを優先すると決めているからです。今回の1つが Astro でした。

移行時に注意した点

一番注意したのは URL です。2018〜2021年に書いた79記事と固定ページ、約1,400枚の画像。記事のパスは1本も変えず、末尾スラッシュまで WordPress 時代と同じに揃えました(Astro 側で trailingSlash: 'always' を固定)。検索流入は過去記事の資産なので、ここだけは妥協しない前提で設計しています。

移行をあきらめた部分もあります。WordPress のすべての機能を静的サイトに持っていくのは手間ばかりかかるので、カテゴリ・タグの一覧ページやコメント機能などは削除することにしました。

公開されている記事の抜き出しについて

記事の抜き出しには WP REST API を使いました。Node.js のスクリプトで /wp-json/wp/v2/posts から記事を、media から画像を取得し、Turndown で HTML を Markdown に変換します。ただし一発では通りません。シンタックスハイライトの <pre> をコードフェンスに直す、広告やシェアボタンの div を除去する、画像URLを自サイトのパスに書き換える。こうしたカスタムルールの追加と再実行の繰り返しは AI に任せました。元記事と変換後の Markdown を AI に比較させ、崩れている箇所を見つけては変換ルールを直させる流れです。スクリプトを冪等に作っておいたので、何度やり直しても壊れないのが効いています。

ちなみに、API から取れた生の JSON はそのままリポジトリに残しました。「元はどう書かれていたか」にいつでも戻れる保険です。

移行しなかったURLは301リダイレクトで対応

記事URLはそのまま静的生成するとして、問題は WordPress 特有のURLです。/category/*/tag/*/*/amp//feed//wp-json/*。この辺りは Cloudflare Pages の _redirects で301リダイレクトにしました。ルールはスクリプトで自動生成し、旧URL全件に curl して期待どおりのステータスが返るかを検証するテストも用意。

カットオーバーは、プレビューURLで全URLを検証してから DNS を切り替え、旧 WordPress を読み取り専用で24時間並走させたうえで停止、という手順です。DNS キャッシュが残っている間に旧サーバへ流れた人を切り捨てないための並走でした。

想定どおりにいかなかったところ

  • デプロイは Cloudflare の Git 連携をやめ、GitHub Actions からのデプロイに変更
  • お問い合わせフォーム(Formspree 予定)は設置自体を取りやめ、連絡導線は X の DM のみに
  • 公開後、sitemap.xml_redirects のワイルドカードに巻き込まれて404になる不具合が発覚。ワイルドカードのルールをやめ、旧 /sitemap.xml だけを新しいサイトマップへ301する単一ルールに変えて修正

こうした対応は Claude Code の SKILL(繰り返し使う作業手順をルール化して AI に渡しておく仕組み)として残しました。似た対応が次に来ても、手順を思い出すところからやり直さずに済みます。

移行してよかったこと

記事の追加は、Markdown を置いた PR をマージするだけになりました。マージのタイミングでビルドとデプロイが走り、リンク切れチェックも CI が持ってくれます。これは助かります。

PR を作成したタイミングでは Cloudflare Pages のプレビューが立ち上がり、公開前に実際の表示で最終確認できるようにしています。Cloudflare の Git 連携を使わず GitHub Actions からデプロイしているのも、これが理由です。

移行の実装自体も Claude Code と壁打ちしながら進め、私は移行した記事を確認するのみ。Claude に記事の差異もチェックさせているので、全記事までは確認していませんが😅

今回の移行で記事の更新は簡単になりましたし、このブログを AI の実験場としても使えるようになりました。ここを土台に、色々試していこうと考えています。

出典