WordPress

WordPressを静的HTML化して本番公開するまで

このブログ(Tonoblog)を、WordPress のまま静的HTMLで公開する構成に切り替えました。記事は今までどおり WordPress で書き、公開されるのは書き出した HTML だけです。表示速度は PageSpeed Insights のスマホで 54点 → 97点 になりました。

きっかけは3つです。

この記事では、制作者向けに「どういう構成にしたか」「静的化すると何が動かなくなり、どう置き換えたか」「本番をどう切り替えたか」を、実際にやった順番でまとめます。サーバーは XSERVER、作業は Mac の Claude Code(AI)と一緒に進めました。

全体の構成

書く場所と公開する場所を分けました。WordPress は Basic 認証をかけた別のサブドメインに移し、記事を書くためだけに使います。公開ドメインに置くのは、書き出した HTML だけです。

Tonoblog の構成図
Tonoblog の構成。書く場所(管理用 WordPress)と公開する場所(静的HTML)を分けている

テーマや書き出し用プラグインのコードは GitHub で管理し、Mac で修正して push → サーバーで deploy.sh を実行すると管理用 WordPress に反映されます。テーマは Snow Monkey をやめて、書き出し専用の軽い自作テーマに替えました。

静的化で動かなくなるものと、その代わり

静的HTMLには PHP もデータベースもないので、「訪問者の操作でサーバーが何かする」機能はすべて動かなくなります。始める前にプラグインと機能を洗い出し、ひとつずつ行き先を決めました。

機能静的化するとTonoblog での対応
お問い合わせフォーム(Snow Monkey Forms)送信できない送信処理だけを自作の PHP 1ファイルに。スパム対策は Cloudflare Turnstile
サイト内検索使えない書き出し時に記事一覧の JSON を作り、JavaScript で検索
コメント投稿できない新規受付は停止。既存のコメントは表示を残す
予約投稿公開時刻に反映されないサーバーの cron で WordPress の定期処理を動かす
日付アーカイブ・添付ファイルページ作らないブログ一覧・親記事へ 301 転送
会員限定ページ・決済関連動かない非公開化、または削除

フォームは外部サービスに任せる方法もありますが、項目が少なかったので自作しました。PHP が動くのは公開側でこの1ファイルだけです。送信先のメールアドレスや Turnstile のシークレットキーは、公開フォルダの外に置いた設定ファイルから読み込んでいます。

書き出しの仕組み

静的化プラグインは既製品もありますが、URL の扱いや転送の細かい要件があったので、WP-CLI で動く書き出し用プラグインを自作しました。wp tonoblog build を実行すると、次の順に処理します。

  1. 書き出す URL の一覧を作る(トップ、記事、固定ページ、カテゴリー・タグ・著者の一覧とその2ページ目以降、検索、フィード、404)
  2. 1 URL ずつ WordPress に描画させ、…/index.html として保存する
  3. HTML 内の管理用ドメインを公開用ドメインに置き換える
  4. HTML と CSS から参照されている CSS・JS・画像だけをコピーする(アップロード画像は丸ごと同期)
  5. .htaccess・robots.txt・sitemap.xml を作る
  6. 検査する(管理用ドメインの残り、展開されていないショートコード、意図しない noindex)。エラーがあれば公開しない

こだわったのは URL を一つも変えないこと です。記事の /年/月/日/スラッグ/ も、日本語のカテゴリー名もそのままです。書き出さない URL(日付アーカイブ、添付ファイルページ、旧サイトマップ、/?s= の検索)は .htaccess で 301 転送し、検索エンジンの評価を引き継ぐようにしました。

もう一つの注意点は、書き出しはサーバー(Linux)上で行うことです。Mac で書き出すと、日本語のファイル名の文字の表し方が変わり、日本語 URL のページが開けなくなることがあります。

Tonoblog の場合、118ページの書き出しは約50秒です。毎回すべて書き出し直す単純な作りにしています。

記事を公開すると、5分以内に自動で反映

静的化で一番面倒になりがちなのが「更新のたびに書き出してアップする」作業です。ここを自動にしたので、書く側の手順は WordPress 時代と変わりません。

  1. 管理画面で記事を公開・更新・削除すると、プラグインが「作り直しの予約」をファイルに1行書くだけにする(管理画面を重くしないため)
  2. サーバーの cron が5分ごとに予約を確かめ、あれば書き出し → 検査 → 公開フォルダへ反映
  3. 結果を管理バーに「静的サイト: 反映待ち/反映中/最新/エラー」と表示し、失敗したときはメールで知らせる

予約の対象は、記事・固定ページ、カテゴリー・タグ、承認済みコメント、サイト名などの設定、プロフィールの変更です。下書きの保存やスパムコメントの自動削除では作り直さないようにしています。

大事にしたのは 失敗しても公開サイトを壊さないこと です。書き出しや検査でエラーが出たら公開せず、前回の状態を残します。公開した中身は直近5回分を保存しているので、すぐに戻せます。予約投稿も、同じ cron が WordPress の定期処理を動かすことで公開時刻どおりに反映されます。

本番の切り替えは、フォルダの入れ替えだけ

切り替えで止まったのは1秒未満です。DNS も公開フォルダの設定も変えていません。やったのは、公開フォルダの中身を「名前の変更」で入れ替えることだけです。

  1. 本番用に書き出し、公開フォルダの隣に置く
  2. WordPress の入った公開フォルダの名前を変え、書き出したフォルダをその名前にする(mv 2回)
  3. WordPress のフォルダは公開領域の外へ移して保管する
  4. 自動更新の反映先を本番に切り替える

大事なのは、旧サイトを消さないこと です。WordPress のファイルも本番のデータベースもそのまま残してあるので、問題があれば逆の入れ替えで数秒で戻せます。切り替えも戻しもスクリプトにし、どちらも「引数なしで確認だけ」「--execute で実行」にしたので、本番の前に何度も練習できました。

切り替え前には次も確かめています。

切り替え後は curl で主要な URL の応答を確かめました。記事や一覧が 200、/?s= や日付アーカイブが 301、/wp-login.php が 404 になっていれば成功です。

静的化しただけでは、54点だった

切り替え直後に PageSpeed Insights で測ると、スマホは 54点、最初の表示(FCP)は13.6秒でした。静的HTMLなのに遅い原因は、管理用 WordPress に残した旧サイトのプラグインが、全ページに大きなファイルを読み込ませていたことです。書き出しは WordPress の出力をそのまま保存するので、不要な読み込みもそのまま残ります。

プラグインは止めず、テーマ側で「その記事の本文で使っているときだけ読み込む」ように絞りました。たとえば、本文にコードブロックがある記事だけが色分けのファイルを読み込みます。フォントは Web フォントをやめて端末のフォント(Mac はヒラギノ、Windows は游ゴシック)にし、CSS・JS・画像にはブラウザに長く保存させる設定を足しました。

トップページ改善前改善後
スマホ パフォーマンス5497
スマホ 最初の表示(FCP)13.6秒0.8秒
スマホ 最大要素の表示(LCP)14.2秒2.2秒
パソコン パフォーマンス6797
パソコン 操作できない時間(TBT)780ミリ秒150ミリ秒
おすすめの方法96100

2026年10月12日、PageSpeed Insights で計測。ユーザー補助と SEO は改善前後とも100点です。残りの TBT はアクセス解析(Google タグマネージャー)の分です。

静的化は「サーバーの処理」をなくすだけで、ブラウザが読み込む量は減らしません。速さを求めるなら、書き出した HTML が何を読み込んでいるかまで確かめるのが大事でした。

WordPress を非公開にして、攻撃される入口はほぼ1つに

公開側で PHP が動くのは、お問い合わせの送信処理 1ファイルだけになりました。WordPress への攻撃の多くは、ログイン画面やプラグインの脆弱性など「サーバーで動くプログラム」を狙います。静的HTMLにはそのプログラムがないので、狙われる場所そのものがなくなります。

攻撃されやすい入口以前(WordPress を公開)今(静的HTML)
ログイン画面(wp-login.php)誰でも開ける。パスワードの総当たりの的存在しない(404)
xmlrpc.php総当たりや大量アクセスに使われる存在しない
REST API(/wp-json/)ユーザー名を調べられることがある存在しない
プラグイン・テーマの PHP脆弱性が見つかると、更新まで狙われる公開側には置いていない
コメント投稿スパムの入口受付停止
お問い合わせプラグインのフォーム自作 PHP 1ファイル(下の対策つき)

残った入口にも対策を重ねています。

ただし、静的化で小さくなったのは公開側の入口です。管理用 WordPress と、同じサーバーにあるほかのサイトは、これまでどおり守る必要があります。

数字で見ると、公開フォルダで動く PHP は 5,879個 → 1個 になりました。

公開側から届くもの以前今
PHP ファイル5,879個1個(お問い合わせの送信処理)
動いているプラグイン約20個(Snow Monkey Blocks / Forms、All in One SEO、SiteGuard など)0個

2026年10月12日にサーバーで数えた値です。プラグインは管理用 WordPress で今も動いていますが、Basic 認証の内側にあるため、公開ドメインからは1つも届きません。

切り替えた後も、攻撃は来ている

切り替え当日のアクセスログを見ると、切り替え後の約4時間半で、WordPress を狙ったアクセスが 17件 来ていました。すべて 403(拒否)か 404(存在しない)で終わり、プログラムには1件も届いていません。

狙われたもの件数結果
ログイン画面(wp-login.php、うちパスワード送信 10件)11403・404
ユーザー名の取得(REST API)・xmlrpc.php2403
存在しないプラグインの PHP(不正プログラムの有無を探る)2404
設定ファイル(.env)・その他の PHP2404

2026年10月12日 10:52~15:13 のログより。他サイトの記事カード(oEmbed)など、攻撃とは言えないアクセスは除いています。

静的化しても、WordPress を狙うアクセスは来続けます。ボットは中身を確かめずに、あらゆるサイトを同じ手順で叩いているからです。変わったのは、叩かれた先に応えるプログラムがないことです。切り替え前の同じ日の午前には、コメント欄へのスパム投稿が6回 PHP まで届いていました。

テーマやプラグインの改修が、Claude Code でできるようになった

もう一つの大きな変化は、カスタマイズのしやすさです。以前は Snow Monkey 用のカスタマイズプラグインや Code Snippets、小さな自作プラグインに変更が分かれていて、どこを直せばいいか探すところから始まっていました。

今は、テーマ・書き出しプラグイン・お問い合わせの処理・公開用のスクリプトがすべて1つの GitHub リポジトリに入っています。Mac の Claude Code に「こうしたい」と頼んで修正し、push して deploy.sh を実行すれば、数分で公開サイトまで反映されます。

つまずいたところ

まとめ

WordPress を管理画面として残したまま静的化すると、書く作業はそのままで、公開側を速く安全にできます。やってみて大事だと感じたのは次の5つです。

  1. 始める前に、動かなくなる機能を洗い出して行き先を決める
  2. URL を変えない。変わるものは 301 で転送する
  3. 更新を自動にし、失敗しても公開サイトを壊さない
  4. 静的化したら、書き出した HTML が何を読み込んでいるか確かめる
  5. コードは GitHub にまとめ、AI で改修できる形にしておく

旧 WordPress はしばらく保管し、問題がなければ整理する予定です。同じように WordPress の静的化を考えている方の参考になればうれしいです。

アバター
この記事を書いた人 トノムラマサシ

masashi Webサイト屋兼ブロガー|過去に企業常駐などを経て現在はフリーランスでディレクションとコーダーとして活動しております。JS大好きなのでJSの仕事依頼お待ちしております。京都在中で5人家族。