このブログ(Tonoblog)を、WordPress のまま静的HTMLで公開する構成に切り替えました。記事は今までどおり WordPress で書き、公開されるのは書き出した HTML だけです。表示速度は PageSpeed Insights のスマホで 54点 → 97点 になりました。
きっかけは3つです。
- 使っていたテーマ(Snow Monkey)の契約を更新しないことにした
- 表示を速くしたかった
- 公開側から WordPress のログイン画面や PHP をなくして、攻撃される入口を減らしたかった
この記事では、制作者向けに「どういう構成にしたか」「静的化すると何が動かなくなり、どう置き換えたか」「本番をどう切り替えたか」を、実際にやった順番でまとめます。サーバーは XSERVER、作業は Mac の Claude Code(AI)と一緒に進めました。
全体の構成
書く場所と公開する場所を分けました。WordPress は Basic 認証をかけた別のサブドメインに移し、記事を書くためだけに使います。公開ドメインに置くのは、書き出した 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 を実行すると、次の順に処理します。
- 書き出す URL の一覧を作る(トップ、記事、固定ページ、カテゴリー・タグ・著者の一覧とその2ページ目以降、検索、フィード、404)
- 1 URL ずつ WordPress に描画させ、
…/index.htmlとして保存する - HTML 内の管理用ドメインを公開用ドメインに置き換える
- HTML と CSS から参照されている CSS・JS・画像だけをコピーする(アップロード画像は丸ごと同期)
.htaccess・robots.txt・sitemap.xmlを作る- 検査する(管理用ドメインの残り、展開されていないショートコード、意図しない noindex)。エラーがあれば公開しない
こだわったのは URL を一つも変えないこと です。記事の /年/月/日/スラッグ/ も、日本語のカテゴリー名もそのままです。書き出さない URL(日付アーカイブ、添付ファイルページ、旧サイトマップ、/?s= の検索)は .htaccess で 301 転送し、検索エンジンの評価を引き継ぐようにしました。
もう一つの注意点は、書き出しはサーバー(Linux)上で行うことです。Mac で書き出すと、日本語のファイル名の文字の表し方が変わり、日本語 URL のページが開けなくなることがあります。
Tonoblog の場合、118ページの書き出しは約50秒です。毎回すべて書き出し直す単純な作りにしています。
記事を公開すると、5分以内に自動で反映
静的化で一番面倒になりがちなのが「更新のたびに書き出してアップする」作業です。ここを自動にしたので、書く側の手順は WordPress 時代と変わりません。
- 管理画面で記事を公開・更新・削除すると、プラグインが「作り直しの予約」をファイルに1行書くだけにする(管理画面を重くしないため)
- サーバーの cron が5分ごとに予約を確かめ、あれば書き出し → 検査 → 公開フォルダへ反映
- 結果を管理バーに「静的サイト: 反映待ち/反映中/最新/エラー」と表示し、失敗したときはメールで知らせる
予約の対象は、記事・固定ページ、カテゴリー・タグ、承認済みコメント、サイト名などの設定、プロフィールの変更です。下書きの保存やスパムコメントの自動削除では作り直さないようにしています。
大事にしたのは 失敗しても公開サイトを壊さないこと です。書き出しや検査でエラーが出たら公開せず、前回の状態を残します。公開した中身は直近5回分を保存しているので、すぐに戻せます。予約投稿も、同じ cron が WordPress の定期処理を動かすことで公開時刻どおりに反映されます。
本番の切り替えは、フォルダの入れ替えだけ
切り替えで止まったのは1秒未満です。DNS も公開フォルダの設定も変えていません。やったのは、公開フォルダの中身を「名前の変更」で入れ替えることだけです。
- 本番用に書き出し、公開フォルダの隣に置く
- WordPress の入った公開フォルダの名前を変え、書き出したフォルダをその名前にする(
mv2回) - WordPress のフォルダは公開領域の外へ移して保管する
- 自動更新の反映先を本番に切り替える
大事なのは、旧サイトを消さないこと です。WordPress のファイルも本番のデータベースもそのまま残してあるので、問題があれば逆の入れ替えで数秒で戻せます。切り替えも戻しもスクリプトにし、どちらも「引数なしで確認だけ」「--execute で実行」にしたので、本番の前に何度も練習できました。
切り替え前には次も確かめています。
- 本番と管理用 WordPress で、記事数・コメント数・最終更新日が一致しているか
- 公開フォルダに WordPress 以外のファイル(検索エンジンの確認用ファイルなど)がないか
- noindex の付き方が旧サイトと同じか
切り替え後は curl で主要な URL の応答を確かめました。記事や一覧が 200、/?s= や日付アーカイブが 301、/wp-login.php が 404 になっていれば成功です。
静的化しただけでは、54点だった
切り替え直後に PageSpeed Insights で測ると、スマホは 54点、最初の表示(FCP)は13.6秒でした。静的HTMLなのに遅い原因は、管理用 WordPress に残した旧サイトのプラグインが、全ページに大きなファイルを読み込ませていたことです。書き出しは WordPress の出力をそのまま保存するので、不要な読み込みもそのまま残ります。
- Font Awesome の JS(約1.5MB)を、アイコンのないページでも読み込んでいた
- フォーム用の reCAPTCHA(約840KB)を、フォームを置き換えた後も全ページで読み込んでいた
- Google Fonts(Noto Sans JP の4つの太さ)が、表示を止めて読み込まれていた
- スライダーやコードの色分け用のファイルも、使わないページで読み込んでいた
プラグインは止めず、テーマ側で「その記事の本文で使っているときだけ読み込む」ように絞りました。たとえば、本文にコードブロックがある記事だけが色分けのファイルを読み込みます。フォントは Web フォントをやめて端末のフォント(Mac はヒラギノ、Windows は游ゴシック)にし、CSS・JS・画像にはブラウザに長く保存させる設定を足しました。
| トップページ | 改善前 | 改善後 |
|---|---|---|
| スマホ パフォーマンス | 54 | 97 |
| スマホ 最初の表示(FCP) | 13.6秒 | 0.8秒 |
| スマホ 最大要素の表示(LCP) | 14.2秒 | 2.2秒 |
| パソコン パフォーマンス | 67 | 97 |
| パソコン 操作できない時間(TBT) | 780ミリ秒 | 150ミリ秒 |
| おすすめの方法 | 96 | 100 |
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ファイル(下の対策つき) |
残った入口にも対策を重ねています。
- お問い合わせの PHP:POST 以外は拒否、送信元ページの確認、Cloudflare Turnstile、人には見えない罠の入力欄、同じ相手からの連続送信の制限。送信先やキーは公開フォルダの外に置く
- 管理用 WordPress:公開とは別のサブドメインに置き、WordPress のログインの前に Basic 認証をかける。検索エンジンにも載せない
ただし、静的化で小さくなったのは公開側の入口です。管理用 WordPress と、同じサーバーにあるほかのサイトは、これまでどおり守る必要があります。
- 管理用 WordPress:本体とプラグインの更新を続け、使っていないプラグインは止める。Basic 認証は推測されにくいパスワードにする。サブドメイン名は SSL 証明書の公開記録から調べられるので、アドレスを隠すだけでは守れない
- 同じサーバーのほかのサイト:同じアカウントのサイトが1つでも乗っ取られると、同じ権限で管理用 WordPress も公開サイトも書き換えられる。Basic 認証は関係ない。使っていないテストサイトは整理する
- サーバーへのログイン:サーバーパネルは二段階認証、SSH は鍵認証にする
数字で見ると、公開フォルダで動く 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件) | 11 | 403・404 |
| ユーザー名の取得(REST API)・xmlrpc.php | 2 | 403 |
| 存在しないプラグインの PHP(不正プログラムの有無を探る) | 2 | 404 |
| 設定ファイル(.env)・その他の PHP | 2 | 404 |
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 を実行すれば、数分で公開サイトまで反映されます。
- 全体を見て直してくれる:コードが1か所にまとまっているので、AI が関係するファイルを自分で探して直せます。この記事の「Turnstile の追加」や「54点→97点の改善」も、それぞれ一つの依頼から始めました
- 変更の履歴が残る:何をいつ変えたかが Git に残るので、おかしくなっても戻せます
- やってはいけないことを先に伝えておける:リポジトリの
CLAUDE.mdに「本番フォルダは直接書き換えない」「秘密情報はコミットしない」などのルールと構成を書いておくと、AI がそれに沿って作業します - 管理画面で直接いじらない:変更は必ずリポジトリから入れるので、「どこで直したか忘れた」がなくなりました
つまずいたところ
- 確認用サイトが 403 になった:書き出し先の作業フォルダを自分だけが読める権限にしていたため、コピーしたファイルも同じ権限になっていました。公開するときにフォルダ755・ファイル644にそろえるようにして解決。
- 海外からの接続が弾かれる:XSERVER の「国外IPアクセス制限」で、SSH や REST API が海外から届きません。サーバーでの作業は日本の回線から行いました。
- 新しい設定を保存しても自動更新が動かない:WordPress は、初めて保存する設定では
update_option_ではなくadd_option_のフックを呼びます。両方に登録して解決。 - スパムコメントの自動削除で、何度も作り直しが走る:表示されている(承認済みの)コメントの変更だけを対象にしました。
- 通知メールが Gmail になかなか届かない:Gmail が POP でメールを取りに行く間隔が長いのが原因でした。XSERVER 側の転送設定に切り替えて、すぐ届くようになりました。
まとめ
WordPress を管理画面として残したまま静的化すると、書く作業はそのままで、公開側を速く安全にできます。やってみて大事だと感じたのは次の5つです。
- 始める前に、動かなくなる機能を洗い出して行き先を決める
- URL を変えない。変わるものは 301 で転送する
- 更新を自動にし、失敗しても公開サイトを壊さない
- 静的化したら、書き出した HTML が何を読み込んでいるか確かめる
- コードは GitHub にまとめ、AI で改修できる形にしておく
旧 WordPress はしばらく保管し、問題がなければ整理する予定です。同じように WordPress の静的化を考えている方の参考になればうれしいです。
