Menu/Prof
スズキマコト
自由人
元々は楽器屋のギター兄ちゃん。
趣味でプログラミングしてるうちに
本職になってしまった人。

過去に喋っていた言語
c pascal Assembler
perl PHP Python Ruby など
javascriptなどは都度必要に応じて。
最近Mac買ったのでswift勉強してます。

体ぶっ壊して死にかけたので人生RESET中。
おすすめ記事!

WordPressを見た目もURLもそのままCloudflareで静的化した

新しいパソコンの画面に、前と同じ見た目のブログ記事ページが2つ並び、サーバーの歯車が雲に変わっているのを、満足そうに眺めるキャラクター

Some visuals are licensed via Canva Pro (includes commercial rights).
Usage complies with Canva’s license terms at the time of use.
License policy: canva.com/policies/content-license-agreement

2026年10月24日の朝、このブログ mlabo.org は WordPress をやめた。今あなたが読んでいるページは、PHP もデータベースも通らない。Cloudflare Pages に置いた、ただの HTML と CSS と JS と画像だ。

それでいて、見た目は前と同じのはずだ。記事の URL も、画像の URL も、カテゴリの一覧も、検索窓も、お問い合わせフォームも、広告も、前と同じ場所にある。変わったのはサーバーだけ。そうなるように、4日かけて Claude と一緒に作り込んだ。

この記事は、その引っ越しの記録だ。共用サーバーで WordPress を運営していて、PHP の騒ぎのたびに振り回されるのはもう嫌だ、でも静的化すると見た目が崩れそう、検索順位が落ちそう、新しい記事をどう書けばいいのか分からない。そう思って踏み切れずにいるあなたに向けて書く。手順だけでなく、途中で何を捨てて、何で作り直して、どこで転んだかまで全部残しておく。

先に言っておくと、長い。材料が多すぎた。

contents

PHPの緊急事態で、共用サーバーのWordPressをやめると決めた

夜の部屋でノートパソコンに向かい、鍵のかかった公開フォルダの画面を見ながら、静かに腹を決めているキャラクター

きっかけは 10月21日の夜、PHP の緊急セキュリティ更新だった。脆弱性の中身はここでは扱わない。運営者として起きたことだけを書く。

rootのない共用サーバーでは、待つしかなかった

俺は 2つのブログを持っていた。ロリポップの mlabo.org と、ABLENET の gpt4jp.com。どちらも WordPress で、テーマは SWELL。そして、どちらも共用サーバーだ。

管理人

さて mlabo と gpt4jp だけど これはどちらもroot持っていない共用サーバーだから 俺ができることは サーバー会社がパッチを当てた対策版のPHPに変更していするくらいしかないよな。

Claude Code に SSH で両サーバーの PHP を確かめてもらった。どちらも修正版が出る前の版で、修正版が出たとしても、それを入れるのはサーバー会社で、俺ではない。共用サーバーでは、PHP の版は契約しているサーバーの提供に任せるしかない。

「要はPHPに届かなきゃいい」:公開フォルダをchmod 000に

エックスサーバーが管理画面ごと止めたという話が流れてきて、ふと思った。

管理人

あとね X serverなんかはサーバー全体をメンテナンスとして すでに回線外したみたいなんだけど要は PHP絡みに攻撃者がアクセスできなきゃいいんだろ? 公開ディレクトリ自体を403にしたらどうだろか

Claude は「その考え方で合っている」と言いつつ、「23時に詳細が出てから止めるか決める方がいい」と慎重な案を出してきた。それも分かる。ただ、外の空気はもう決まっていた。

管理人

どうも サーバー止めとけって意見が’多いんで(X serverga が 先陣きって完全停止したからな

18時56分、両サーバーの公開フォルダを chmod 000 にした。フォルダが読めなければ、PHP が動く前に Web サーバーがエラーを返す。戻すのは chmod 755 の 1回だけで、データには一切触れない。

ちなみに 403 にするつもりが、返ってきたのはほとんど 404 だった。フォルダが読めないので、Web サーバーが「ファイルがない」と扱ったわけだ。ヘッダーから X-Powered-By: PHP/… が消えたので、PHP が止まっていることは確かめられた。止まり方の見た目は違ったが、止まったものは止まった。

止める作業の見回りでは、公開フォルダの外に 2022年の WordPress 一式(PHP ファイル約 2,800個)が消し忘れのまま残っていたのも見つかった。

管理人

ああ それ削除しちゃっていいよ 使ってないし。 しっかり見つけてきたな’しかしw

緊急対応のはずが、押し入れの掃除まで始まった夜だった。

振り回されるのはもうやめる:記事を選んで静的化へ

修正版が各サーバーに入るまでの時間は、サーバー会社ごとに違う。どちらの会社が悪いという話ではない。共用サーバーの利用者は、自分の都合では動けない。それが今回よく分かった。

止めたまま待つこと、ほぼ一日。mlabo.org を修正済みの PHP で戻せたのは翌日の夕方だが、その少し前に腹が決まった。10月22日の 16時18分、Claude にこう書いた。

管理人

結局さ 俺は codexを使用しないので この サイトでの継続は考えていない。
ただ mlabo.orgとgpt4jp.comのコンテンツを静的化して クラウドフレアに公開するって方向にしたいんだよ。
デザイン的にも何もかも新しくしたいし 俺は今回の php騒動で無駄な時間を費やすのに合理性を感じられないので ガチ目に mlabo.orgとgpt4jp.com の中から記事を厳選して クラウドフレアに 静的公開物として移動したいと考えているんだ。
俺の場合 検索順位とか今んとこどうでもいいので笑(そもそもあんまり気にしてない)
張り付いている アドセンスやアマゾンアソシエイトは気にするけどね笑
これを君のプロダクトとして作りたいんだよ

冒頭の「codex」の話には前史がある。9月に一度、Codex の Sites という機能で 3記事だけ静的化を試したことがあった。そのときは SWELL の見た目を再現できず、独自デザインで押し切られて終わっている。今回は Claude Code で、自分の Cloudflare の上に、自分のものとして作る。

ドメインについては、Cloudflare に持っていたテスト用の pages.dev のアドレスを貼って、一瞬こう言った。

管理人

ぶっちゃけドメインこれでもいいんだよな笑

1分後に自分で覆した。

管理人

あ でも認証の手間考えると mlabo.orgを使うべきか。

AdSense の承認は mlabo.org に付いている。ドメインを変えたら審査からやり直しだ。笑ってる場合じゃなかった。

Cloudflare を有料プランにした方がいいか、という相談もした。月5ドルくらい砦の使用料として安いものだと思ったのだが、Claude の答えはこうだった。

クロード君

それに、今回の移行で守りが一番固くなるのは、お金ではなくサイトの作りのほうです。静的な HTML には PHP もデータベースもログイン画面もないので、今回の騒ぎのような攻め口そのものがなくなります。

有料プランで増えるのは Worker を動かせる量で、守りの強さは変わらない。だから無料で始める。これで方針は決まった。WordPress をやめ、選んだ記事を、mlabo.org のまま Cloudflare の静的サイトにする。

見た目は作らず持ってくる:SWELLの記事を静的HTMLで元どおりにする

元のページと作り直したページが左右に並んだ画面を、虫めがねで見比べて、細かい違いを探しているキャラクター

静的化と聞くと、多くの人は「WordPress のサーバー移転」の親戚だと思うかもしれない。移転なら、同じ WordPress を別の場所で動かすだけだ。静的化はそうではない。WordPress と PHP が、ページが開かれるたびにその場でやっていた仕事を、公開前に全部やっておく。つまり「表示のたびに何が作られていたか」を、ひとつ残らず洗い出す作業になる。

止めたまま回収し、生きているうちに「正解」を保存する

最初にやったのは回収だ。サイトを止めたまま、Claude Code に SSH で公開フォルダを数秒だけ所有者限定(700)にしてもらい、データベースのダンプ、wp-content/uploads 一式、ads.txt を Mac に落とした。終わるたびに 000 に戻す。画像は mlabo が 12,618 ファイル、gpt4jp が 4,796 ファイル。サーバー側で数えた数と一致した。

管理人

止めたままsshで回収しようか。

回収したデータベースには、お問い合わせの記録のような個人情報も入っている。だからこれは手元の別の場所に置き、静的サイトの Git には一切入れない。取り込むのは、選んだ記事の本文と画像と広告と SEO の情報だけだ。

そしてもう一つ、後から効いてくる準備をした。まだ公開中だった mlabo.org の記事 190本を、ログインしていない読者が見る HTML のまま全部保存したのだ。これが「正解」になる。元のサイトが消えた後では、二度と取れない。

新デザインの最初の版を全部捨てた理由

ここで最初の回り道をした。静的化を決めたとき、俺は「デザインも新しくしたい」と言っている。だから最初の版は、SWELL の部品を自前の部品とクラス名に置き換え、仮の CSS を当てる設計だった。Claude Code は Rust で取り出しと生成の道具 mlabo-site を書き、全 272本の変換まで通した。

その報告を見て、俺はこう言った。

管理人

生成される静的HTMLだけどさ 極力オリジナルに準拠して欲しいんだ。 これはUIなんで 極端に変わると困るぜ? だからCSSなんかも捨てずに当てられるものはしっかり当ててくれないデザインが寄らないと思うんだよな。 ちなみに codexはこれがうまくできなかったので結局 オリジナルデザインみたいなので押し切られた笑

言い出したのは俺で、覆したのも俺だ。でも出来上がりを見て分かった。読者が見慣れた画面が変わるのは、引っ越しではなく別のサイトになることだ。Claude は自作の部品をすべて消し、方針を正反対に書き換えた。約1時間半の作業が消えたが、ここで捨てたのは正解だった。

WordPressが表示のたびに作っていた部分だけを再現する

新しい方針はこうだ。見た目は作らない。持ってくる。

  • 記事の本文は、データベースにある SWELL のブロック HTML を、クラス名ごとそのまま出す。
  • CSS と JS は、公開中のページが実際に読み込んでいたもの(LiteSpeed が結合したファイル)をそのまま持ってくる。ヘッダー、サイドバー、フッターの HTML も同じ。
  • WordPress と SWELL が表示のたびにその場で作っていた部分だけを、SWELL や Pochipp や WordPress 本体の PHP を読んで、同じ HTML を出すように Rust で書き直す。

この「その場で作っていた部分」が思った以上に多い。吹き出し、記事カード、Pochipp の商品ボックス、広告タグ、ブログパーツ、FAQ、投稿リスト、引用符やダッシュの自動整形(wptexturize)、画像の幅と高さ、YouTube や X の埋め込み(データベースに残っていたキャッシュを使う)、画像の拡大表示、そして Pochipp Pro のセール表示まで。セール表示は WordPress が表示した時刻で切り替えていたので、ページを開いた日本時間で同じ判定をする JS にした。

記事の原本は「記事パッケージ」という形で Git に置いた。1記事 = 1フォルダで、変換前の SWELL の HTML、題名や日付や SEO の情報、画像の一覧が入っている。変換は生成のたびに行うので、変換を直せば全記事にまとめて効く。生成は毎回サイト全体を作り直し、約2秒で終わる。同じ原本から 2回作ると、全ファイルが 1バイトも違わない。

3本から154本へ:比較ツールで差を1本ずつ潰す

作り直した版を、保存しておいた正解と比較ツールで突き合わせた。最初に本文が完全に一致したのは、190本中たった 3本だった。

そこから、違う所を 1つずつ原因までたどる。この記号の違いは WordPress の自動整形、この属性の違いは画像の幅高さの付与、この入れ子は SWELL のグループブロック、という具合に、WordPress が裏でやっていた処理を 1つ足すごとに数字が動く。3本、77本、149本、154本。

途中で一番首をかしげたのは、生成したページだけ目次が空になる件だった。原因は旧サーバーのキャッシュプラグイン LiteSpeed で、SWELL の JS が待っている「読み込み完了」のイベント名を、LiteSpeed が自分用の名前に書き換えていた。持ってきた JS はその書き換え後のもので、静的サイトにはその名前のイベントを出す者がいない。元の名前に戻したら目次が出た。比べる相手がいなければ、たぶん気づかなかった。

並べた画面を見て、俺はつい言った。

管理人

しかし君は見事に同じデザインだけだなやっぱ賢いなぁ
GPTはこれができなかったんだぜ ていうか頼んでもやろうとしなかった

クロード君

ありがとうございます。うまくいったのは、賢さというより比べる相手がまだ生きていたからです。

これがこの章の一番大事なところだ。元のサイトが生きているうちに正解を保存して、差を 1本ずつ潰す。最終的に移した記事では 173本中 140本の本文が完全に一致し、残りの差はどれも理由が分かっている。外した記事へのカードを消した、もう手放すドメインへのリンクを外した、旧サーバー側がかけていた圧縮やメールアドレスの隠し処理、のどれかだ。

ミラーと「偽クラウドフレア」:手元に2つの基準を置く

その日、俺は出先にいた。ふと思いついて送った。

管理人

ローカルのドッカーにさLAMP環境作ってあるんだけど
そこにさっき持ってきたDBと画像を差し込んだらミラーサイトをローカルに作れるんじゃね?

クロード君

できます。しかも、やる価値はかなり高いです。

旧サーバーを解約した後も、元のサイトを手元で開ける。見比べの基準が残る。Claude Code は Docker に mlabo と gpt4jp のミラーを立てた。URL の書き換えは WP-CLI の search-replace で、データベースの中の値を壊さないように。キャッシュやログイン保護、外へ通信するプラグインは止め、手元の Mac からしか開けないようにした。ミラーの本文は 190本中 169本が公開ページと一致。完全なミラーではないが、基準としては十分だ。

もう一つ、Cloudflare 公式の wrangler pages dev で、Cloudflare Pages と同じ配信の仕組みを手元に作った。アカウントもいらない。転送ファイル _redirects も、末尾にスラッシュがない URL の転送も、404 の返し方も、本番と同じ規則で試せる。

管理人

今あるのは mlaboとgpt4jpのそれぞれのミラーと偽クラウドフレアってことで良いな?😆

以後、この呼び名が正式名称になった。「ブログの環境を起動して」と言えば全部立ち上がる道具も作ってもらった。

ただし、ここに来るまでに Docker には何度か転ばされた。Docker Desktop が管理者パスワードの入力待ちで黙って止まる。書類フォルダに置いたデータを読もうとして、macOS のアクセス確認で止まる(原因が分かるまで約16分)。アプリを終了させて止めたら、画面だけ閉じてエンジンが残り、応答しなくなる(約20分)。

Mac で同じことをするなら、データはホーム直下に置き、止めるときは Docker Desktop のコマンドを使うといい。

Googleに引っ越しを気づかせない:URLから画像・一覧・検索まで元と揃える

静的化を決めたとき、俺は「検索順位はどうでもいい」と言った。でも作っているうちに考えが固まった。順位を上げたいのではない。今あるものを、引っ越しで壊したくないのだ。

大前提は「変わるのはサーバーだけ」

管理人

大前提はなGoogleの検索エンジンが構造に変更なしと思うようにしなければいけないわけよ わかるか?

クロード君

わかります。Google から見て「サーバーが替わっただけで、サイトは何も変わっていない」状態にする、ということですね。

たとえるなら、引っ越しても住所も表札も変わらない家だ。外から見た住所が同じなら、転送の届けも、評価のやり直しもいらない。変わるのは中の設備だけ。もちろん、これで順位が変わらないことまでは保証できない。切り替え後に Search Console で様子を見る必要はある。それでも、こちらから変化を見せないことはできる。

移す記事はSearch Consoleで選ぶ

全 272本を 1本ずつ選ぶつもりで、Claude に選定用のチェックリストまで作ってもらっていた。ところが Search Console のインデックスの画面を開いた瞬間、気が変わった。

管理人

この インデックスに登録されない記事はこの際全部やめてしまえと笑

Google に登録されていない記事は、外しても検索には何の影響もない。Claude Code が登録済みの URL の一覧を取り出して記事の番号と突き合わせ、「クロール済み・未登録」「検出済み・未登録」の件数とちょうど一致することで照合を確かめた。外すのは 25本(mlabo 17、gpt4jp 8)、残すのは 247本。閲覧数の多い記事も何本か外れたが、一覧で見たうえで、それでいいと決めた。選定は数分で終わった。外した記事は、新しいサイトでは 404 のままにしてある。

「大前提違反」:画像のURLとsrcsetを作り直す

翌朝、生成した記事の画像の URL を見て手が止まった。/14099/images/2026-10-h1.webp のような、記事ごとのフォルダになっている。元の WordPress は /wp-content/uploads/年/月/ファイル名 だ。

管理人

その大前提違反なんだよ これ 多分 今直さないとやばいね

画像の URL が変われば、Google の画像検索も、よそのサイトから張られた画像のリンクも切れる。住所を変えないと言ったそばから、画像だけ引っ越していたわけだ。

直すのに約2時間かかった。画像は元の URL のまま出す。WordPress が作っていた縮小版(-1536x864 のような名前のもの)も、同じ名前で同じ場所に出す。画面の幅に合わせて画像を選ばせる srcset と sizes は、WordPress 本体の計算(wp_calculate_image_srcset)を Rust に移し、WordPress がデータベースに記録していた画像の情報の順番どおりに並べた。最後の詰めで順番が合わなかった原因は、Rust の JSON のライブラリがキーを勝手に並べ替えていたことだった。

結果、移した 173本中 156本で、記事の画像の URL が元と完全に一致した。残りは、外した記事へのカードを消した分など、理由が分かっているものだけだ。

一覧・RSS・サイトマップ・旧URLの別の形・検索・404

その後、Claude にまだ元と違う所を並べてもらってから、俺は言った。

管理人

まず プレビューは クラウドフレアに見立てた偽サーバーなんだから ここが完璧にならないと どうにもならんからな

偽クラウドフレアで完璧でなければ、本物でも完璧にはならない。そこで、記事以外も全部元と揃えた。

  • カテゴリ・タグ・年・月の一覧と、その 2ページ目以降(/page/2/)。元どおり検索結果には出さない設定も含めて。
  • RSS(サイト全体、カテゴリ、タグ、著者)と、サイトマップ(元のプラグインと同じファイル名と形)。
  • /?p=10656 や /?cat=32 のような WordPress 時代の URL の別の形。静的サイトの転送ファイルは URL の ? 以降を見られないので、トップページだけを受け持つ小さな関数(Pages Functions)で転送している。
  • head の中身。説明文の作り方まで SEO SIMPLE PACK と同じにして、173本すべてで説明文と OGP とキーワードが元と一致した。
  • サイト内検索。/?s=語 でも、WordPress の正式な形の /search/語/ でも動く。検索の決まり(語の AND、"語句"、先頭が - の語の除外、並び順)に加えて、元のデータベースが大文字小文字、全角半角、ひらがなとカタカナ、濁点の有無を同じとみなしていたのも、元のデータベースで実際に試して合わせた。7つの検索語で、元の記事の顔ぶれがすべて一致した。

一つ、静的サイトならではの落とし穴がある。Cloudflare Pages は 404.html を置かないと、存在しない URL にトップページを「200 OK」で返す。消えた記事が、検索結果にトップページの中身で残りかねない。生成のたびに必ず 404.html を作るようにした。

公開前のリンク切れ検査

最後に、生成したサイトのすべてのページと CSS が指しているサイト内のリンク、画像、JS、CSS、フォントが、本当に存在するかを調べる検査を作った。1つでも切れていたら公開しない。

これが思った以上に役に立った。外した記事へのカード、まだ移していなかった固定ページ、旧サイトのドキュメントルートに置いてあった配布ファイルやフォントが、一度に洗い出された。固定ページは「どこからもリンクされていないものは移さない」と決め、リンクの数を数えて判断した。旧サイトでもすでに切れていたリンクは、リンクだけ外して文字を残した。

新しい記事はどう書くのか:手元のWordPressを編集室として残す

手元の編集部屋でブロックエディタに記事を書き、机の上の大きな丸いボタンを押そうとしているキャラクター

ここまでで、既存の記事はほぼ元どおりに出せるようになった。Claude からの報告も順調そのものだった。そこで俺は、ずっと引っかかっていたことを聞いた。

「新しい記事の処理がすっぽ抜けている」

管理人

問題は今も少し触れたけど新しい記事を作った時の処理がすっぽ抜けている。完全に。
いままでの静的化を見るとワードプレスにあるものを引っ張り出して静的化だろ?
んじゃさクラウドフレアに静的化したものに新たに記事を追加するとどう処理するつもりだ?
ワードプレスがそのページを作らないと新記事デプロイできないのに君は気が付いてる?

今の俺の書き方はこうだ。記事を作る自前の仕組み RelayPress が WordPress に下書きを入れる。俺がブロックエディタで目で見て確かめ、Pochipp の商品カードや関連記事のカードを足し、公開ボタンを押す。WordPress は「最終確認の画面」と「部品を置く道具」と「公開ボタン」を一人で担っていた。WordPress を消すと、その 3つが丸ごと消える。

静的化の記事は、既存記事を HTML にする話で終わりがちだ。でも本当の難所はここにある。

編集用WordPressと会話の2本立て

Claude が出してきた案の一つが、手元の WordPress を編集専用に残す方式だった。

管理人

その案Aを君に提案するつもりでこの話をしてた。笑
ただな君と会話しながらインタラクティブにあれやこれややるルートも欲しいんだよね。
専用のエディタ作るのは大変だからやめておこうよ
どうせまともに動く物作るまでにすごく時間かかりそうだ笑

クロード君

同じ案を考えていたなら話が早いです(笑)。専用エディタは作りません。新しい記事の流れは、次の 2 本立てにします。

1本目は、手元の Docker に「編集用 WordPress」を置き、今までどおりそこで書いて仕上げる道。2本目は、Claude との会話で仕上げる道。WordPress は消さない。読者の前から下げて、俺の作業部屋に移すだけだ。

ボタン1つで取り込み・build・プレビュー

ボタンを作ると言われて、つい突っ込んだ。

管理人

ボタンにするにしろ どこに表示するつもり? UIないじゃんな

置き場の案を 3つ出してもらって、即答した。

管理人

1と2だな 君との会話でも ボタンでもできることが正しいUXだ

今の流れはこうなっている。

  1. 編集用 WordPress で記事を書き、Pochipp やカードを足して公開する。
  2. 管理画面の上の「新サイト」ボタンを押す。
  3. Mac の中で、編集用の内容の取り込み → サイト全体の生成 → リンク切れ検査 → 偽クラウドフレアへの反映が、まとめて走る。
  4. 偽クラウドフレアで見て問題なければ、「Cloudflare に公開」ボタンで本番に出す。

ブロックエディタで書いて公開ボタンを押す、という手触りはほぼ変わらない。変わったのは公開ボタンの先で、WordPress のデータベースではなく、静的サイトの生成に行く。ただ、編集用 WordPress は手元の Mac にあるので、ボタンは Mac の前でしか押せない。出先からは会話で「公開して」と頼めば、同じ処理が走る。

同期は「編集用の状態をそのまま映す」に変えた

最初、Claude はこの同期を慎重に作っていた。原本はあくまで Git の中の記事パッケージで、編集用 WordPress で記事を消したり書き換えたりしても、勝手には反映せず「ずれ」として止めて報告する。安全側の設計だ。

試しに記事を作って消してみて、それに気づいた。

管理人

これは ケアレスミスの原因になるので 編集で変化したものは全て プレビューにも影響するようにしてくれ。 削除も追加も変更も記事公開も非公開もだ

消したはずの記事がプレビューに出続けたら、いつか必ず間違える。作る側の安全より、使う人間がミスしない方を取った。今は、ボタンを押した時点の編集用 WordPress の公開状態が、そのままプレビューに出る。消した記事は別の場所に退避していて、公開し直せば元の閲覧数や画像を引き継いで戻る。Claude Code は原本の写しで全 254件を「変わった」扱いにして取り込み直し、中身が元と同じになることまで確かめた。

記事番号がURLになる落とし穴と、記事作成の道具の投稿先

編集用 WordPress を作るときに、地味だが致命的な落とし穴を一つ踏みかけた。

gpt4jp から移す 74本も編集用に入れる必要があった。番号がぶつからないように、最初は 900000番台に置こうとした。ところが WordPress は、次の記事にその続きの番号を振る。新しいサイトでは記事の番号がそのまま URL なので、次に書く記事が /901101/ になってしまう。普通の番号で入れ直し、原本との対応は記事ごとの隠しフィールドで取ることにした。

もう一つ、RelayPress の投稿先も変えた。今までは本番の mlabo.org に下書きを入れていたが、そこにはもう WordPress がない。投稿先を手元の編集用 WordPress に向け直したところ、正しい鍵なのにログインしていない扱いで弾かれた。WordPress は HTTPS でもなく「手元の環境」とも宣言していないサイトでは、外部からの投稿に使うアプリパスワードを無効にする。編集用 WordPress に WP_ENVIRONMENT_TYPE を local と設定して通った。

手元の WordPress に記事作成の道具から投稿するなら、最初に入れておくといい。

お問い合わせ・メール・広告を、WordPressなしで動かす

お問い合わせの封筒を、迷惑送信よけの関所を通してメールボックスへリレーのように手渡しているキャラクター

WordPress のプラグインに任せていたものは、ほかにもある。お問い合わせフォーム、ドメインのメール、そして広告とアクセス解析だ。

Googleを使わないお問い合わせ:Turnstile、Functions、Worker

元のお問い合わせは Contact Form 7 だった。静的サイトにはフォームを受け取る PHP がない。

管理人

そうか お問い合わせは 今まで プラグインでやっていたんだっけな。 これなんか 代替手段あるか? Googleのやつは使いたくないからな

外部のフォームサービスを使う手もあったが、Cloudflare だけで作ることにした。分担はこうだ。

  • 迷惑送信よけは、元のフォームでも使っていた Cloudflare の Turnstile と、同意のチェック。
  • 送信を受けるのは Pages Functions(サイトに付属する小さな関数)。必須項目、長さ、メールアドレスの形、同意、Turnstile の結果を確かめる。
  • 通ったものだけを、別に置いた Worker に渡す。Worker が Cloudflare の Email Routing で、サイトのドメインのアドレスから俺のメールに送る。返信先は送ってくれた人のアドレスにしてあるので、届いたメールにそのまま返信できる。

わざわざ Worker を分けたのは、Pages Functions からはメール送信の機能が使えないからだ。送信だけを Worker に任せ、Worker 自体は外から呼べない設定にしてある。フォームの中身はどこにも保存しない。残るのは届いたメールだけだ。

手元で試すときは、Turnstile の公式のテスト用の鍵を使い、手元の Worker はメールを送らずにファイルに書き出す。中身の確認、Turnstile なしで弾かれること、入力の誤りで項目ごとの理由が返ること、JS を切っても送れることまで、手元で確かめた。

ドメインのメールをCloudflareに移す順番

試し公開の段取りを相談していたとき、俺はこう言っていた。

管理人

とりあえずメールは普段からそんなに飛んでこないのでしばらく停止してもいいレベルだ笑

数分後には、結局メールも移す話になっていた。@mlabo.org 宛てのメールは、それまでロリポップが受けていた。ロリポップを解約すれば届かなくなる。

Email Routing を有効にしようとしたら、最初は断られた。旧ホスティングの MX レコード(メールの届け先)が残っていると有効にできない。旧 MX を消し、すぐに有効化し(MX と、なりすまし対策の SPF と DKIM が自動で入る)、転送するのは info@ の 1つだけというルールを作る。消してから届くまでの空白ができるので、続けて一気にやる。実際には 3分で終わった。外のアドレスから送った試験メールが Gmail に届いたのを確かめて完了。戻すときは、Email Routing を無効にして旧 MX を入れ直せばいい。

AdSenseと解析は本番のドメインのときだけ読み込む

試し公開を見て「もうこのまま公開してもいいレベルだ」と思ったとき、Claude に止められた。広告と解析がまだ入っていない。

AdSense は DNS ではなく、サイトのルートにある ads.txt と、全ページの広告コードでつながっている。ドメインが同じ mlabo.org なので、審査のやり直しはいらない。ただ、これまでの解析と広告のタグは、WordPress の Site Kit というプラグインが入れていた。WordPress をやめれば、それも一緒に消える。

そこで、Google アナリティクス、AdSense、同意のメッセージ、Microsoft Clarity を、1つの JS からまとめて読み込むようにした。読み込むのは、表示しているアドレスが mlabo.org のときだけ。手元のプレビューや試し公開では読み込まないので、作業中の自分の閲覧を数えないし、許可していないアドレスで広告を求めることもない。手元と試し公開で、それらへの通信が 0件であることも確かめた。Site Kit 特有のタグの書き方は外し、各サービスの標準の形にそろえている。

試し公開からDNSの切り替えまで:本番に出す手順と戻し方

試し公開の側線を横に見ながら、新しい線路へ切り替えるレバーに手を置き、戻し方も確かめているキャラクター

いよいよ本物の Cloudflare に出す。ここからは、外に影響する操作ばかりなので、1つずつ俺が承認して進めた。

人に見られない試し公開

まず、Claude Code から Cloudflare を操作する道具 wrangler にログインした。ブラウザで Cloudflare にログインしていても、wrangler は別にログインが要る。俺は Codex 用にしか設定していなかったのに、すっかり済んだつもりになっていた。

試し公開は、引っ越しの前に新しい家を家族だけで内見するようなものだ。順番はこうした。

  1. Cloudflare Pages のプロジェクトを空で作る。
  2. プレビューを Cloudflare Access で制限する設定を、先に入れる(ここは俺が押した)。
  3. プレビュー用のブランチに出す。
  4. ログインなしでアクセスすると、サインイン画面に飛ばされることを確かめる。

そのうえでログインして、トップ、記事、一覧とページ送り、固定ページ、RSS、サイトマップ、旧 URL の転送、検索、画像の拡大表示まで、本物の Cloudflare の上で確かめた。読み込みのエラーは 0。

切り替え直前に気づいたpages.devの重複

試し公開とメールの確認が終わって、気分はもう引っ越し当日だった。

管理人

ていうかさ もう本番環境にしちゃってもいいんじゃねこれ

管理人

あとは DNS書き換えたら いけるんじゃね?

Claude の説明に「WordPress を公開する」みたいな言い回しが混ざったので、突っ込んでおいた。

管理人

これ 公開するサイトはワードプレスじゃないぞ。

管理人

ただの静的ファイルのサイトだろ?笑

クロード君

はい、ほぼただの静的ファイルです(笑)。HTML、CSS、JS、画像が 4,766 ファイル並んでいるだけです。

正確には、お問い合わせ、検索、旧 URL の転送だけは小さな関数が動く。それ以外は本当にファイルが並んでいるだけだ。

ところが切り替えの直前に、Claude が見落としに気づいた。本番に出すと、同じ中身が mlabo.pages.dev という Cloudflare のアドレスにも出る。プレビューには自動で「検索結果に出さない」印(noindex)が付くが、本番のブランチのアドレスには付かないし、Access の制限も効かない。放っておけば、本物のドメインと同じ中身のサイトがもう一つ検索に出かねない。生成のたびに書き出す _headers に、pages.dev のアドレスにだけ noindex を付ける規則を入れた。本番は本番用の鍵で作り直し、pages.dev 側で中身と noindex を確かめてから先へ進んだ。

本番公開ボタンが自分で止まった日

順番が前後するが、編集用 WordPress の「Cloudflare に公開」ボタンにも、切り替えの直後に一度だけ本番で働いてもらった。このボタンには安全装置がある。

  • 本番は、本番用の鍵で作り直す。プレビューは Turnstile のテスト用の鍵で作っているので、そのまま送るとお問い合わせが動かない。
  • リンク切れを調べ、1つでもあれば止まる。
  • 作り直したものが、確かめたプレビューと鍵以外まったく同じかを比べる。違えば止まる。

ボタンと同じ経路で本番に出してみたら、いきなり止まった。

クロード君

プレビューは 5:18 に作ったものなので、この1行が入っていません。そのまま進めると、確かめていない変更が全ページに出てしまうところでした。こういうときに止めるための確認が、狙いどおりに働いたことになります。

原因は、フッターに足された 1行「Powered by Cloudflare」。足したのは、少し前に別の会話で俺が頼んだものだった。安全装置の最初の獲物は、持ち主のいたずら書きだったわけだ。

管理人

Powered by Cloudflare も入れていい。プレビューを作り直してから、ボタンと同じ経路で公開して

管理人

それが表示されてれば 新しいサイトに接続してるってのも一発でわかるしな

プレビューを作り直して公開。送られたのは変わった 415ファイルだけで、残りの 4,351ファイルは送信済みのものが使い回された。今このページの下にも、その 1行がある。旧サイトか新サイトかを目で見分けられる、ちょっとした目印だ。

DNSを切り替える、wwwも忘れない

切り替えそのものは、拍子抜けするほど短い。

  • Pages のカスタムドメインに mlabo.org を追加すると、Cloudflare が「A レコードを Pages 向けの CNAME に置き換える」という変更案を出す。承認した内容と同じかを確かめて有効にする。
  • www を忘れない。www.mlabo.org は、パスとクエリを保ったまま mlabo.org に 301 で飛ばす転送ルールを作り、www のレコードを転送専用の値(AAAA の 100::、プロキシあり)に置き換えた。旧サーバーを向いたままだと、解約した日に www が切れる。

切り替えた後、トップ、記事、一覧、固定ページ、検索、フィード、サイトマップ、旧 URL の転送、404、http から https への転送、www からの転送を確かめた。本番には noindex が付いていないこと、canonical が正しいこと、広告枠が埋まり、解析が読み込まれることも。証明書は Cloudflare が自動で発行して、自動で更新する。

そして、本番のお問い合わせフォームから自分で送ってみた。Gmail に届いた。Turnstile の本番の鍵は本番のドメインでしか動かないので、ここは切り替えた後でなければ確かめられなかった。フォームの確認を待たずに先に切り替えた理由もそこにある。

AIにDNSを触らせる許可と、戻し方

一つ書いておきたいことがある。DNS の変更は、Claude Code の側の安全確認に何度か止められた。メールの切り替えのとき、俺は 2時間ほど出かけていて、外から聞いた。

管理人

今リモートなんだけどさ俺 俺が許可しても作業できない?

通ったのは、俺が「どのレコードを、どうしてよいか」を具体的に書いた指示文を送ったときだった。ふわっとした「やっていいよ」では通らない。

面倒に思うかもしれないが、DNS はサイトの住所そのものだ。AI に触らせるなら、対象と操作を名指しする。その一手間で、何をしたかの記録も残る。

戻し方も、切り替える前に決めておいた。Pages のカスタムドメインを外し、旧サーバーの A レコードに戻すだけ。これは旧サーバーを解約するまで使える。デプロイした版は、Cloudflare の管理画面から 1操作で前に戻せる。メールは Email Routing を無効にして旧 MX を戻す。戻し方が分かっていると、切り替えのボタンは意外と軽く押せる。

2つのブログを1つにまとめ、旧サーバーを片付ける

2つの引っ越し箱を1つの棚の箱にまとめ、片付けを終えてほっとしているデフォルメ姿のキャラクター

今回の引っ越しには、もう一つ仕事があった。gpt4jp.com を mlabo.org にまとめることだ。2つのブログを 1つにするときに、最初に決めておくべきことがある。

吹き出しのIDをそろえる:表情の矛盾は許す

管理人

mlaboとgpt4jpで吹き出しのIDに相違があったから これは 共通化しないといかんな。

SWELL の吹き出しは、本文の中で番号で呼ばれる。2つのブログで同じ番号が別の顔を指していた。広告タグも、ブログパーツも、商品も、記事カードのリンク先も、番号がぶつかる。

決めたのは、mlabo の番号を基準にすること。同じ顔は mlabo の番号に寄せ、mlabo にない顔だけ新しい番号を足し、それ以外の gpt4jp の番号には頭に g を付けた。gpt4jp の記事は /g/<番号>/、画像は /g/wp-content/uploads/… に置いている。

後日、RelayPress の投稿先を直していたとき、Claude が「gpt4jp は宛先がないので止める」と筋を立てていたので、結論を言わずに質問を重ねた。

管理人

そのドメイン対応する理由は 基本的に吹き出しのID違いのためだろ?

サイトを分けていた一番の理由は、吹き出しの番号の違いだった。それならもう、分ける理由はない。

管理人

とにかく 既存の吹き出しIDは 優先が mlaboで設定してたもの IDがダブるものは 多少表情に矛盾でても気にしないので mlaboの吹き出しで表示すればいい。
で mlabo側にないIDがあるのが困るんだよ それがあるなら 君の方で対応しろ 先方のワードプレスに該当IDと画像を追加しろ

完璧を求めず、困ることだけを直す。調べてみると、編集用 WordPress に欠けていた吹き出しは 2つだけで、その 2つを足して終わった。RelayPress からも gpt4jp を新しい記事の行き先として選べないようにした。最後の投稿だけで止めるのではなく、最初の質問の段階で選べなくしないと、書いた後で無駄になる。

gpt4jp.comは転送専用のPagesで301

gpt4jp.com から移した 74本には、それぞれ旧 URL から 301 の転送を付けた。mlabo-site が、記事ごとの転送、画像の転送、お問い合わせなどは同じページへ、それ以外はトップへ、という 228行の転送ファイルを作る。これを転送専用の小さな Pages プロジェクトに置き、gpt4jp.com をそのカスタムドメインにした。サーバーを解約しても、ドメインの期限が来るまでは転送が効く。

ここでも一つやらかした。最初のデプロイを、静的サイトの Git のフォルダから実行したので、本体用の関数まで転送専用のプロジェクトに入ってしまった。転送だけのプロジェクトは、空のフォルダから出す。誤ったデプロイは消して出し直した。

順番にも気をつけた。転送先の mlabo.org/g/… が存在してから、転送元の gpt4jp.com を切り替える。74本すべてが転送され、転送先がすべて 200 を返すことを確かめた。

それから、手放すドメインは他人が取得できる。新しいサイトに残る記事から gpt4jp.com へのリンクやカードは、生成のときに外すようにした。gpt4jp の設定にだけあった Amazon のトラッキング ID も、ドメインと一緒に消えるので mlabo 側に差し替えてある。

旧サーバーの解約と、旧サーバーのために作った仕組みの片付け

gpt4jp.com は、移行が終わるまでの間だけ、元の WordPress を公開に戻しておいた。記事を新しいサイトに移し、転送が動くのを確かめたら、ABLENET の契約は終える。その時点の計画は、新サイトと転送ができたらすぐ解約だった。ところが確かめると、ABLENET は年契約で、途中で解約しても返金がない。

管理人

返金はないので期限までほっておくにしよう

DNS を Cloudflare に向けた時点で、ABLENET の WordPress は外から見えなくなっている。急いで解約する理由はない。

ロリポップは、年払いを更新日の前に月払いに替えてあったので、mlabo.org の住所もメールも移ったこと、記事が使っていた配布ファイルやフォントが新サイトで出ていることを確かめてから、解約を決めた。解約すれば旧サーバーへの戻し方もなくなるので、確かめる順番はそこだけ慎重にした。

片付けで意外と忘れていたのが、旧サーバーのために作った仕組みだ。証明書の更新に合わせて Cloudflare の設定を切り替える見張りの定期実行が、Claude 側にも Codex 側にも残っていた。DNS を書き換えられる API トークンも 3つ残っていた。切り替えた後で俺が思い出し、全部消した。トークンやパスワードの削除は、俺が自分の手で押した。

やってみて分かった落とし穴と、静的化して変わったこと

gpt4jp.com を開くと、mlabo.org に一瞬で飛んだ。

管理人

OK だよ コレで終わり?

クロード君

サイトの引っ越し自体は、これで終わりです。mlabo.org は新しい静的サイトで動いていて、gpt4jp.com からの転送、メール、お問い合わせ、広告、アクセス解析も本番で確かめ終わりました。

最初に決めておけばよかったこと

4日間で大きく作り直したのは 3回。どれも「最初に決めていれば避けられた」ものだ。あなたが自分のサイトでやるなら、作り始める前にこの 3つを決めてほしい。

  1. 見た目は元のままか、新しくするか。 元のままなら、作らずに持ってくる。そして元のサイトが生きているうちに、全ページを正解として保存する。
  2. 新しい記事をどう書くか。 今の書き方を 1本分たどってみて、WordPress が担っていた役(確認の画面、部品を置く道具、公開ボタン)をどこに移すか決める。俺の答えは、手元の WordPress を編集室として残すことだった。
  3. 何を元と揃えるか。 記事の URL だけでなく、画像の URL、縮小版、head、一覧とそのページ送り、RSS、サイトマップ、旧 URL の別の形、検索、404 まで。検索エンジンから見て変わるのはサーバーだけ、を基準に 1つずつ確かめる。

その下に、細かい落とし穴がいくつもある。キャッシュプラグインが JS を書き換えていた件、404.html がないとトップが 200 で返る件、記事番号の続きが飛ぶ件、本番の pages.dev に noindex が付かない件、転送専用のプロジェクトに関数が入った件。どれも、比べる相手と、公開前の検査と、戻し方があったから小さな事故で済んだ。

「実際無茶苦茶速いよw」

切り替えた後、最初に感じたのは速さだった。

管理人

いあ 実際無茶苦茶速いよw

クロード君

よかったです(笑)。ページを作るサーバーがなくなって、できあがったファイルを近くの拠点から返すだけになったので、体感で分かるくらい違いますよね。

測った数字はない。俺の体感だ。でも理屈は単純で、ページが開かれるたびに PHP がデータベースを読んで組み立てていた仕事が、もうない。

ほかにも変わったことがある。サイトは約 4,800ファイルで、Cloudflare Pages の無料プランの上限(1回 2万ファイル)にはまだ余裕がある。生成は約 2秒で、同じ原本からは毎回同じものができる。証明書の更新を気にする必要もなくなった。そして何より、次に PHP の騒ぎが来ても、このブログには止めるものがない。最初に Claude が言ったとおり、守りが一番固くなったのは、お金ではなくサイトの作りだった。

まだ確かめていないこと

正直に書いておく。まだ済んでいないこともある。

  • RelayPress で一から作った本物の記事を、編集用 WordPress で仕上げて本番に公開するまで、通しで動かしたことはまだない。手作りの試験用の下書きで投稿を確かめただけだ。
  • 新しい記事で外部サイトのカードを使ったときに、その中身を取り込む仕組みはまだない。
  • 切り替えた後の Search Console、アクセス解析、AdSense が数日たってどうなるかは、これから見る。

それでも、引っ越しは終わった。共用サーバーで PHP の修正を待ちながら公開フォルダを閉じた夜から、4日後のことだ。

あなたのブログでも、同じことはできると思う。テーマが SWELL でなくても考え方は同じで、表示のたびに作られている部分を洗い出し、元のサイトを正解にして、変わるのはサーバーだけにする。まずは、元のサイトが生きているうちに、全ページを保存するところから始めてみてほしい。それが、あとで一番効いてくる。

シェアしてくれると喜びます
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

makotoのアバター makoto Blogger&YouTuber

サーバー管理者として17年ほど仕事でサーバー触ってました。
www,mail,dns,sql各鯖をすべてFreeBSDで運用してましたが現世ではかなりレアなタイプになるみたいですね笑

viやシェルスクリプトとかperlとかgccとかFreeBSDとか実はbashよりtcshが好きとか時々寝ぼけるのは
その名残でしょう。

今まで縁の下の力持ち的な他人のためにプログラムを書き他人のためにサーバー構築し他人のためにWEBサイトを創る的な世界から
自分の好きなことに集中できる環境は実に気持ち良いですね。
現役は引退済みなので難しいことはやりませんしやれません。

現在 ほぼ自由人。

contents