
Summary
この文書の要点
- 全体移行ではなく、独自テーマ、許可したオプションと投稿、実際に参照されるメディアだけを同期し、本番限定データを保持します。
- 送信側と受信側の専用プラグインをHTTPS、Application Password、manage_optionsで結び、差分確認から再開転送、反映、スモークテスト、ロールバックまでを一つの処理にします。
- 投稿の照合では同期元IDの対応メタを正とし、別の同期元に対応付け済みのIDやスラッグを再利用しないことが、上書きによる欠落を防ぐ要点です。
- 差分は前回ハッシュではなく、URL変換後の期待値と本番の実データを比較し、変換規則の改善や本番側の手編集も検知します。
全体移行が、必要以上に広い変更になっていた
開発環境のWordPressを本番へ反映する方法として、サイト全体をまとめて移行するプラグインは分かりやすい選択肢です。しかし、ファイル、データベース、プラグイン状態、ユーザーを一括で置き換える方式は、移したい範囲が限定されているほど影響範囲が広くなります。全体移行が途中で不安定になり、復旧のためにプラグインをすべて無効化した後、メディア参照まで切れた経験から、移行単位そのものを見直しました。
もう一つの問題は、アップロード先の差です。開発環境は標準の`/wp-content/uploads`を使っていても、本番は運用上の理由から独自のアップロードパスを持つことがあります。データベース内のURLやファイルパスをそのまま移すだけでは、ファイルが存在しても画像や動画が表示されません。
必要だったのは、サイトを複製する仕組みではありません。本番にだけ存在するユーザーや申込情報を残しながら、開発側で管理する限定データだけを、安全に繰り返し反映できる仕組みでした。
- 全体移行の失敗は、復旧対象もサイト全体へ広がりやすい。
- アップロードパスとURLの差は、ファイル転送だけでは解消しない。
- 本番を開発環境の複製にせず、管理対象だけを同期する。
SSH・FTPなし、追加費用なし、本番限定データは削除しない
前提は、SSHとFTPを利用できず、追加費用のかかる外部サービスにも依存せず、WordPress管理画面から運用を完結させることです。管理者が専用プラグインを導入し、管理画面で接続確認、差分確認、反映、結果確認、必要時のロールバックを行える構成にしました。
同期対象は、独自テーマ、明示的に許可したオプション、イベント相当のカスタム投稿、技術・活用コラム相当のカスタム投稿、専用メタ、分類、アイキャッチ、本文やメタから実際に参照される画像・動画・PDFです。WordPress本体、ユーザー、申込情報、他プラグインの設定と有効状態、無関係なメディアは対象外です。
削除も自動では伝播させません。開発側に存在しない投稿やファイルが本番にあっても、それだけを理由に消さない方針です。同期を『同一化』ではなく『管理対象の更新』として定義すると、本番限定データを守る境界が明確になります。
- 同期対象は種類だけでなく、オプション名、投稿タイプ、メタキー、分類、参照メディアまで許可リストで定義する。
- ユーザー、申込情報、他プラグイン、WordPress本体は更新しない。
- 本番限定データを保持するため、存在差だけを根拠に削除しない。
同じ専用プラグインを送信側と受信側へ置く
送信側と受信側には、同じ専用WordPressプラグインを導入します。送信側は同期対象を走査してマニフェストと期待値を作り、管理画面に差分を表示します。受信側は認証済みのREST APIを通じて、状態確認、反映準備、ファイル受信、適用、ロールバックを担当します。
REST APIは、状態確認、準備、4MiB単位の再開可能なファイル転送、反映、ロールバックという責務に分けました。一つの長いリクエストですべてを処理せず、転送と適用を分けることで、共有サーバーの実行時間制限や一時的な通信失敗から再開しやすくします。
送信側が本番データベースへ直接接続することはありません。WordPressの権限判定、保存先解決、投稿APIを受信側自身に実行させることで、環境固有の設定を受信側のWordPressへ閉じ込めます。
- 送信側:対象抽出、期待値生成、差分表示、処理の進行管理。
- 受信側:権限確認、保存先解決、バックアップ、受信、適用、復旧。
- 転送と適用を分離し、失敗時に最初から送り直さない。
HTTPS、Application Password、manage_optionsを重ねて保護する
接続にはHTTPSとWordPress Application Passwordを使います。Application Passwordによる認証が成功しただけでは反映を許可せず、各REST APIの権限確認で`manage_options`を要求します。専用アカウントの権限が不足している場合や、認証情報が無効な場合は、準備や転送より前に処理を拒否します。
Application Passwordは通常のログインパスワードと分けて発行でき、用途ごとに失効させられます。送信側で保持する値は、WordPressの認証キーから導出した鍵で暗号化し、送信側データベース内の専用設定へ保存します。通常パスワードや平文の認証情報をログ、Git、配布ファイルへ残しません。
認証キーを変更すると、既存の暗号文を復号できなくなる設計です。その場合は接続情報を再登録します。秘密情報を復元可能なまま長期間持つ以上、Application Passwordの用途名、発行者、失効手順も運用手順に含めます。
- 通信路はHTTPSに限定する。
- 認証と権限を分け、Application Passwordに加えてmanage_optionsを確認する。
- 通常パスワードを使わず、保存値、ログ、Gitの各経路で平文を残さない。
事前確認から完了までを、同じ順序で実行する
運用手順は『事前確認 → 差分表示 → 本番反映 → スモークテスト → 完了』に固定しました。事前確認では、受信側のバージョン、権限、書込可否、PHP要件、アップロード先、二重実行ロックの状態を確認します。差分表示では、追加、更新、転送、再利用の件数を管理者が反映前に確認します。
反映を開始すると、受信側は今回上書きする範囲をバックアップし、デプロイ単位の識別子とロックを作ります。ファイルは不足分だけを転送し、すべてのチェックサムが一致した後に、マニフェストで許可されたメディア、分類、投稿、オプション、テーマを適用します。
適用後は、公開ページ、対象投稿、画像・動画などの代表URLをスモークテストします。途中の検証や適用後確認に失敗した場合は、今回のバックアップから自動ロールバックし、成功扱いにしません。
- 管理者が適用前の差分を読めることを公開条件にする。
- 不足ファイルの転送完了とチェックサム一致を、適用開始の条件にする。
- 反映成功とスモークテスト成功を分け、後者まで通って初めて完了とする。
ファイルは4MiBずつ送り、相対パスとSHA-256で照合する
テーマや参照メディアのファイルは、相対パス、サイズ、SHA-256をマニフェストへ記録します。受信側に同じ相対パスとSHA-256のファイルがあれば転送しません。内容が異なるファイルだけを4MiB単位で送り、受信済み位置から再開できるようにします。
各チャンクはデプロイ単位、対象ファイル、オフセットを検証し、一時領域へ追記します。最終チャンクの受信後にファイル全体のSHA-256を再計算し、マニフェストと一致しない限り適用候補へ移しません。通信の成功と内容の一致を別々に確認するためです。
受信側が扱う相対パスは正規化し、`..`、絶対パス、想定外のルートを拒否します。管理者権限を持つAPIであっても、テーマやアップロード領域の外へ書き込めない境界を先に設けます。
- 相対パスとSHA-256が一致する既存ファイルは再転送しない。
- チャンク転送はデプロイ、ファイル、オフセットを照合して再開する。
- パストラバーサルとチェックサム不一致は適用前に拒否する。
投稿は専用の同期元IDで対応付け、本番限定投稿を残す
投稿の対応付けには、同期元の投稿を識別する専用メタを使います。受信側で同じ同期元IDを持つ投稿が見つかれば、その投稿を更新します。見つからなければ新規作成し、受信側の自動採番IDとは独立した対応関係を保存します。
同期するのは、許可した投稿タイプ、投稿本体、専用メタ、分類、アイキャッチです。受信側にだけ存在し、同期元IDを持たない投稿は保持します。この規則により、本番側で追加された投稿を消さずに、開発側で管理する投稿だけを繰り返し更新できます。
対応メタには一意性が必要です。別案件へ応用する場合は、マニフェスト内の重複だけでなく、受信側ですでに同じ同期元IDが複数の投稿へ付いていないかも準備段階で検査し、曖昧な状態のまま自動修復しない設計が安全です。
- 正となるキーは受信側の投稿IDではなく、専用メタに保存した同期元ID。
- 同期元IDがない本番限定投稿は保持する。
- 対応関係が重複・競合している場合は、反映前に停止して確認する。
メディアの保存先はwp_upload_dir()だけから解決する
メディアは、開発環境の絶対パスやURLを受信側へ持ち込みません。送信側ではアップロード基準からの相対パスをマニフェストへ記録し、受信側では`wp_upload_dir()`が返す保存先とURLを唯一の情報源として実ファイルと公開URLを組み立てます。これにより、標準パスと独自アップロードパスの差を吸収します。
画像は添付ファイルの代表画像だけでなく、WordPressが生成するレスポンシブ画像の派生サイズと原本もマニフェストへ含めます。動画やPDFも、対象投稿やメタから実際に参照されるものだけを収集します。サイト内のアップロード領域を丸ごと複製しないため、無関係なファイルを巻き込みません。
同じ相対パスとSHA-256のファイルがすでにあれば再利用します。ファイルだけ存在し、WordPressの添付登録がない場合は、重複転送せず、その既存ファイルから添付情報と必要なメタデータを登録します。ファイルの存在と添付投稿の存在を同じものとして扱わないことが重要です。
- 絶対パスや開発URLではなく、アップロード基準からの相対パスを運ぶ。
- 派生サイズ、原本、参照される画像・動画・PDFを一つのマニフェストで扱う。
- 既存ファイルを再利用し、添付登録だけが欠けている状態も回復する。
同じ数値IDを初回移行候補にした結果、26件が欠けた
初回移行では、まだ専用メタによる対応付けがありません。そのため、同期元IDと同じ数値の受信側投稿IDがあれば同じ投稿とみなすフォールバックを入れていました。既存サイトを引き継ぐための互換策でしたが、受信側の自動採番IDと衝突しました。
先に新規作成した投稿へ、後で処理する別の同期元投稿と同じ数値IDが自動採番されることがあります。後続処理がそのIDを初回移行候補として再利用すると、先に作った投稿を別の内容で上書きします。処理件数上は100件を同期したように見えても、最終的には26件が欠けました。
この失敗は、APIの成功件数やループの処理件数だけでは完全性を確認できないことも示します。同期元IDの集合、受信側に保存された対応メタの集合、対象投稿数を照合しなければ、上書きによる欠落を検出できません。
- 自動採番IDは環境ごとに変わり、同期キーにはならない。
- 成功レスポンス数と、反映後に存在する一意な投稿数は別の指標。
- 件数だけでなく、同期元IDの集合一致を検証する。
対応付け済みの候補は、別の同期元に絶対再利用しない
修正後は、専用メタによる正しい対応付けを最優先します。数値IDやスラッグによるフォールバックを使うのは、候補投稿に同期元IDがまだ付いていない初回移行だけです。候補がすでに別の同期元IDへ対応付けられていれば、数値IDやスラッグが一致しても再利用しません。
判定は『候補が見つかったか』ではなく、『その候補を今回の同期元へ割り当ててもよいか』まで行います。マニフェストの処理中に新しく作った投稿も直ちに対応付け済みとして扱い、後続投稿が初回フォールバックで奪わないようにします。
スラッグも同じです。本番側で人が作った投稿と偶然同じスラッグになる場合があります。未対応の投稿だけを移行候補にし、判断できない衝突は自動で上書きせず、差分画面へ競合として出す方が安全です。
- 優先順位は、正しい同期元ID対応 → 未対応の同一ID → 未対応の同一スラッグ → 新規作成。
- 別の同期元IDが付いた候補は、IDやスラッグが一致しても再利用しない。
- 自動解決できない競合は、上書きせず反映前の確認対象にする。
アップロードURLを先に変換し、ループバックURLを後で除く
もう一つの失敗は、CTAなどの本文に古い開発URLが残ったことです。現在の開発サイトURLだけを本番URLへ置換しても、以前使っていた別ポートの`localhost`は一致しません。`127.0.0.1`やIPv6ループバックを使ったURLも同じ問題を持ちます。
変換は二段階にしました。最初に、送信側のアップロード基準URLを受信側の`wp_upload_dir()`から得た基準URLへ置換します。その後、`localhost`、`127.0.0.1`、IPv6ループバックの任意ポートを持つHTTPまたはHTTPS URLを、受信側のホームURLへ置換します。
順序が重要です。ループバックURLを先にホームURLへ変えると、アップロードファイルの相対部分を正しく保てない場合があります。意味が明確なアップロード基準URLを先に処理し、残った開発ホストを一般規則で除くと、変換範囲を説明しやすくなります。
- 現在の開発URLだけでなく、過去に使ったループバックURLも検査する。
- アップロード基準URLの変換を、一般的なホスト変換より先に行う。
- 任意ポートとIPv4・IPv6のループバックを検証ケースに含める。
差分は前回ハッシュではなく、変換後の期待値と実データで判定する
当初は、前回の反映時に保存したハッシュと今回の送信値を比べれば、変更の有無を判断できると考えていました。しかし、URL変換規則を改善しても送信前の値が同じなら差分になりません。本番側で手編集された値も、前回ハッシュだけを見る方式では見落とします。
差分判定は、今回の変換規則を通した期待値と、受信側に現在保存されている実データを直接比較する方式へ変更しました。投稿本文、対象メタ、許可したオプションなどを、受信側で保存される形まで正規化してから比べます。
この方式なら、変換規則の改善は期待値の変化として現れ、本番の手編集は実データとの差として現れます。前回状態は監査や説明には使えますが、現在の反映要否を決める正としないことが要点です。
- 期待値は、現在の変換規則を適用した後の保存形式で作る。
- 比較対象は、前回の記録ではなく本番に現在存在する実データ。
- 差分理由を、送信値変更、変換規則変更、本番側変更に分けて表示できるようにする。
反映直前の限定バックアップを3世代保持する
適用前には、独自テーマ、対象オプション、対象投稿、今回上書きするメディアを保護領域へバックアップします。毎回サイト全体を複製するのではなく、このデプロイが変更する可能性のある範囲を戻せる形で保存し、直近3回を保持します。
ロールバックは、テーマだけ、投稿だけと個別に判断するのではなく、同じデプロイ単位で保存したスナップショットへ戻します。途中まで適用された状態で一部だけ戻すと、投稿とメディア、オプションとテーマの期待関係が崩れるためです。
3世代の限定バックアップは、短時間で自動復旧するための仕組みです。サーバー障害、データベース全体の破損、同期対象外データの誤操作まで戻すものではありません。ホスティング側のバックアップや別媒体への定期バックアップとは役割を分けます。
- バックアップ範囲は、今回の書込対象と一致させる。
- 適用単位と復旧単位をそろえ、部分的な戻しで整合性を崩さない。
- 限定バックアップを、サイト全体の災害復旧手段の代わりにしない。
テーマは検証済みの一時領域から入れ替える
テーマは、受信したファイルを公開中のディレクトリへ順次上書きしません。まず一時領域でPHP構文、必須ファイル、マニフェストのSHA-256、必要なPHPバージョン、反映先の書込権限を確認し、すべて通った成果物だけを入替対象にします。
入替直後の致命的エラーには、通常のプラグインより早い段階で読み込まれるMUプラグインのガードを使います。更新直後のリクエストで致命的エラーを検知した場合は、退避していた旧テーマへ自動で戻します。管理画面自体を開けなくなる故障に、管理画面からの手動操作だけで対処しないためです。
構文検査だけで動作保証にはなりません。テンプレートの実行、必要な関数、プラグインとの連携、公開ページのHTTP応答は、入替後のスモークテストで別に確認します。
- 公開中テーマへ直接チャンクを書き込まない。
- PHP構文、必須ファイル、SHA-256、PHP要件、書込権限を入替前に確認する。
- 致命的エラーの自動復帰と、公開ページのスモークテストを分ける。
正常系だけでなく、拒否と再開を検証する
安全性の検証では、正常に同期できることだけでなく、危険な入力や途中失敗を拒否・回復できることを確認します。相対パスを抜ける指定、チェックサム不一致、権限不足、認証失敗は、対象ファイルや本番データを変更する前に止めます。
同じ受信側へ二つの反映が同時に走ると、バックアップと適用順序が混ざります。そのため、受信側ではデプロイ単位のロックを取り、二重実行を拒否します。ロックには有効期限と状態確認を持たせ、通信切断で永続的に操作不能にならない回復経路も必要です。
再開転送では、すでに受信した位置とチャンク内容を照合し、同じチャンクの再送を安全に扱います。再試行できることと、重複適用しないことを同時に検証します。
- 拒否系:パストラバーサル、チェックサム不一致、認証失敗、権限不足。
- 競合系:二重実行ロック、期限切れロックからの回復。
- 再試行系:中断位置からの転送再開、同一チャンク再送、適用の一回性。
独自アップロードパスと本番限定投稿を含む条件で検証した
検証には、本番相当の独自アップロードパスを持つWordPress環境を用意しました。技術・活用コラム相当のカスタム投稿100件、イベント相当の投稿13件、参照メディア622ファイルの規模で、初回反映と再実行を確認しました。
既存ファイルは相対パスとSHA-256で再利用され、再転送は0件でした。ID照合の修正後は、投稿、分類、アイキャッチが重複なく反映され、上書きによる欠落がなく、本番限定投稿も保持されることを確認しました。
初回反映だけでなく、同じデプロイの再実行で差分が0になること、ロールバック、再反映、画像と動画の代表URLがHTTP 200を返すことまで確認しました。『一度成功した』ではなく、『戻せる』『同じ入力を繰り返せる』『参照先から取得できる』を別々の合格条件にしています。
- 規模:対象カスタム投稿100件、イベント相当13件、参照メディア622ファイル。
- 差分:既存ファイルの再転送0件、同一デプロイ再実行の差分0件。
- 完全性:投稿、分類、アイキャッチの一意な対応と本番限定投稿の保持。
- 復旧性:ロールバック、再反映、画像・動画URLのHTTP 200。
全体移行、SSHデプロイ、管理画面差分同期を使い分ける
新しい本番環境を開発環境の完全な複製として作り、ユーザーや注文などの本番データがまだ存在しないなら、全体移行の方が単純です。反対に、テーマと限定コンテンツだけを継続的に管理し、本番で増えるデータを保持するなら、許可リスト型の差分同期が適しています。
SSHやデプロイ基盤を利用できる場合は、署名・検証した成果物をサーバー側で切り替える方式の方が、ファイル操作と監視を組み立てやすいことがあります。管理画面完結の方式は、SSH方式の上位互換ではなく、接続手段が限られた環境でWordPress自身を実行境界にする選択です。
判断軸は、接続手段より先に、何を正としてどこまで同期するかです。サイト全体、コードだけ、限定データを含む差分のどれが必要かを決め、削除、衝突、ロールバックの意味をその単位に合わせます。
- 全体移行:本番を完全に置き換えてよく、環境差が小さい初回構築向け。
- SSH等の成果物デプロイ:コード中心で、サーバー側の切替と監視を設計できる環境向け。
- 管理画面差分同期:本番限定データを残し、許可したWordPressデータも継続反映する環境向け。
この方式が扱わない範囲を先に決める
この仕組みは汎用的なデータベース移行ではありません。WordPress本体、一般プラグイン、プラグイン独自テーブル、ユーザー、申込や注文などのトランザクションデータは対象外です。これらを変更する案件では、個別の移行設計、停止時間、整合性検査が必要です。
4MiBの再開転送でも、共有サーバーの容量、実行時間、HTTP制限、WAF、PHPメモリ上限はなくなりません。大容量ファイルや大量変更では、準備・転送・適用の上限、再試行回数、一時領域の回収を環境ごとに検証します。
二重実行ロックは、デプロイ同士の競合を防ぐもので、編集者による同時更新までは止めません。対象投稿を本番で直接編集する運用があるなら、反映時間帯、競合表示、どちらを正とするかを決めます。また、削除を伝播しない設計では、不要投稿やファイルの整理は別の承認付き操作が必要です。
- プラグイン独自テーブルや取引データには、別の移行方式を用意する。
- HTTP転送の上限と一時領域を、実際のホスティング条件で測る。
- 本番での手編集と削除は、同期とは別の競合・承認ルールを持つ。
安全性は転送方法より、同期の境界と期待状態で決まる
SSHやFTPが使えないことは、全体移行しか選べないことを意味しません。WordPressのREST API、権限、保存先解決を使い、同期対象を許可リストで限定すれば、管理画面を入口にした差分デプロイを設計できます。
ただし、転送できるだけでは不十分です。投稿を環境固有の数値IDで結ばないこと、アップロード先を`wp_upload_dir()`から解決すること、URL変換後の期待値と本番の実データを比べること、反映と復旧を同じ単位で設計することが重要です。
別案件へ応用するときは、最初に『本番で保持するデータ』『開発側を正とするデータ』『衝突時に自動更新しない条件』を書き出します。この三つが決まれば、API、マニフェスト、バックアップ、検証の境界を具体化できます。


