「ローカルで作ったWordPressサイトを本番に反映したい。でもプラグインだとファイルサイズ制限に引っかかるし、phpMyAdminを毎回触るのも不安……」。こうした悩みを持つ方は多いのではないでしょうか。
この記事では、WP-CLI の wp db コマンドと rsync を使い、ローカル環境から本番環境へ安全にデプロイする方法を解説します。プラグインに頼らず、コマンドラインだけで完結するため、ファイルサイズ制限もなく、何度でも再現可能です。
GUI 操作の移行プラグインに比べると敷居が高く感じるかもしれませんが、仕組みを理解すれば「データベースを出す → URLを置換する → ファイルを同期する」というシンプルな3ステップに集約されます。一度フローを作ってしまえば、以降のデプロイは数分で完了します。
デプロイ全体のフロー
ローカルから本番へのデプロイは、次の4ステップで進めます。
- 本番DBのバックアップ(ロールバックの保険)
- ローカルDBをエクスポート → 本番へインポート
- URL置換(
wp search-replaceでローカルURLを本番URLに書き換え) - rsync でファイル同期(テーマ・プラグイン・アップロード画像をまとめて転送)
特に重要なのは ステップ1のバックアップ です。本番のデータベースを上書きする操作なので、万が一の際にロールバックできる状態を必ず作ってからデプロイに入ります。
プラグイン移行との違い
All-in-One WP Migration などのプラグインは手軽ですが、無料版ではインポートサイズに上限があり、画像が多いサイトではすぐに壁にぶつかります。また、毎回 WordPress 管理画面を開いてエクスポート → インポートという手作業が発生するため、デプロイ頻度が上がると時間のロスが大きくなります。
一方、WP-CLI + rsync の組み合わせはサイズ制限がなく、コマンド一発で実行できる点が強みです。シェルスクリプトに組み込めば、デプロイをさらに効率化できます。
| 比較項目 | プラグイン移行 | WP-CLI + rsync |
|---|---|---|
| ファイルサイズ制限 | 無料版は上限あり(通常512MB) | 制限なし |
| 操作 | GUI(管理画面) | CLI(ターミナル) |
| 再現性 | 毎回手動 | スクリプト化で自動 |
| URL置換 | プラグインが自動処理 | wp search-replace で明示的に実行 |
| 差分転送 | 非対応(全量) | rsync が差分のみ転送 |
| 必要スキル | 低い | SSH・コマンドライン操作 |
事前準備
必要なもの
- ローカル環境の WordPress(Herd、Local、Docker など何でもOK)
- WP-CLI(ローカルと本番サーバーの両方にインストール済み)
- SSH接続(本番サーバーへSSHでログインできること)
- rsync(Windowsの場合はWSL2を推奨。macOS標準版は古いため、Homebrew版の利用を推奨)
macOS の rsync は Homebrew 版を使う
macOS に標準で入っている rsync はバージョンが古い(2.6系)ため、オプション互換や挙動差でハマることがあります。Homebrew 版の新しい rsync を使うのが安全です。
# Homebrew が未導入なら先にインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# rsync をインストール
brew install rsync
# バージョン確認(3.x ならOK)
rsync --version
Homebrew 未導入時は、インストール完了後に表示される案内に従って PATH を通してください。which rsync の出力が /opt/homebrew/bin/rsync(Apple Silicon)または /usr/local/bin/rsync(Intel)になっていれば、Homebrew版を使えています。
WP-CLI の導入確認
ローカルと本番の両方で WP-CLI が使えるか確認します。
# ローカル
wp --version
# 本番サーバー(SSH接続後)
wp --version --allow-root
バージョン情報が表示されれば準備完了です。まだインストールしていない場合は、WP-CLIの使い方ガイドを参照してください。
SSH接続の設定
rsync や WP-CLI のリモート実行にはSSH接続が必要です。毎回パスワードを入力しなくて済むように、~/.ssh/config で接続設定をまとめておくと便利です。
# ~/.ssh/config
Host my-server
HostName 203.0.113.10
User deploy
Port 22
IdentityFile ~/.ssh/id_ed25519
設定後は ssh my-server でパスワードなしにログインできることを確認します。
ステップ1:本番DBのバックアップ
デプロイ前に、現在の本番データベースを必ずバックアップします。ここを省くと、問題が起きたときに元に戻す手段がなくなります。
# 本番サーバーでDBをエクスポート(SSHログイン後)
wp db export /tmp/prod-backup-$(date +%Y%m%d%H%M%S).sql --path=/var/www/html
--path=/var/www/html は「どのWordPressインストールに対してコマンドを実行するか」を示す指定です。wp-config.php がある WordPress ルートを指します。SSHで入った直後のカレントディレクトリに依存しないため、誤ったサイトを操作しにくくなります。
タイムスタンプ付きのファイル名にしておくと、複数のバックアップが混ざりません。
SSHでログインせずにリモート実行することも可能です。
# ローカルからSSH経由でリモート実行
ssh my-server "wp db export /tmp/prod-backup-$(date +%Y%m%d%H%M%S).sql --path=/var/www/html"
ステップ2:データベースの移行
ローカルDBのエクスポート
wp db export でローカルのデータベースをSQLファイルに書き出します。
# ローカル環境で実行
wp db export /tmp/local-db.sql --path=/path/to/local/wordpress
本番へのインポート
エクスポートしたSQLを本番サーバーのデータベースにインポートします。方法は2通りあります。
方法A:SQLファイルを転送してからインポート
# SQLファイルを本番サーバーに転送
scp /tmp/local-db.sql my-server:/tmp/
# SSHログインしてインポート
ssh my-server "wp db import /tmp/local-db.sql --path=/var/www/html"
方法B:パイプで直接インポート(転送とインポートを一発で)
# ローカルのSQLをパイプでリモートに流し込む
cat /tmp/local-db.sql | ssh my-server "wp db import - --path=/var/www/html"
方法Bのほうがファイル転送のステップを省けてスマートです。ただし、中間ファイルが残らないため、問題が起きた場合の切り分けは方法Aのほうがやりやすいでしょう。
ステップ3:URL置換(search-replace)
データベースをそのままインポートすると、記事本文やウィジェット、設定値に含まれるURLがローカルのまま(例:https://mysite.test)になっています。これを本番のURL(例:https://example.com)に書き換える必要があります。
この置換には wp search-replace を使います。phpMyAdmin や SQL の REPLACE 関数で直接書き換えるのは危険です。WordPress のデータベースにはシリアライズされたデータ(PHPオブジェクトを文字列化したもの)が多数格納されており、単純な文字列置換ではデータが壊れます。
# 本番サーバーで実行
wp search-replace 'https://mysite.test' 'https://example.com' \
--all-tables \
--precise \
--recurse-objects \
--path=/var/www/html
各オプションの意味
| オプション | 説明 |
|---|---|
--all-tables | WordPress標準テーブルだけでなく、プラグインが作成したテーブルも対象にする |
--precise | カラム単位で正確に置換(誤置換を防ぐ) |
--recurse-objects | シリアライズされたデータ内も再帰的に置換する |
エスケープ済みURLの置換
WordPress のデータベースには、URLがスラッシュをエスケープした状態(https:\/\/mysite.test)で格納されている箇所があります。JSONベースのブロックエディタデータなどがこれに該当します。通常のURLに加え、エスケープ形式でも置換を実行しましょう。
# エスケープ済みURLの置換
wp search-replace 'https:\/\/mysite.test' 'https:\/\/example.com' \
--all-tables \
--precise \
--path=/var/www/html
キャッシュとパーマリンクのフラッシュ
URL置換のあとは、キャッシュとパーマリンクをフラッシュして古い値が残らないようにします。
wp cache flush --path=/var/www/html
wp rewrite flush --path=/var/www/html
ステップ4:rsync でファイル同期
データベースの移行が完了したら、次はファイルの同期です。rsync を使えば、変更のあったファイルだけを差分転送できるため、初回以降の同期は非常に高速です。
rsync は rsync [options] 転送元 転送先 の順で指定します。以下の例では、/path/to/local/... が転送元(ローカル)、my-server:/var/www/html/... が転送先(本番)です。
また、ディレクトリ末尾の / 有無で挙動が変わります。themes/ のように末尾スラッシュありだと「中身だけ」を同期し、themes のように末尾スラッシュなしだと「ディレクトリごと」同期します。意図しない階層ズレを防ぐため、同期先・同期元ともに末尾 / を付ける運用がおすすめです。
テーマの同期
rsync -az --delete \
-e "ssh" \
/path/to/local/wp-content/themes/ \
my-server:/var/www/html/wp-content/themes/
プラグインの同期
rsync -az --delete \
-e "ssh" \
/path/to/local/wp-content/plugins/ \
my-server:/var/www/html/wp-content/plugins/
アップロード画像の同期
rsync -az --delete \
-e "ssh" \
--exclude='cache/' \
/path/to/local/wp-content/uploads/ \
my-server:/var/www/html/wp-content/uploads/
rsync の主要オプション
| オプション | 意味 |
|---|---|
-a | アーカイブモード。パーミッションやタイムスタンプを保持して再帰的にコピー |
-z | 転送時に圧縮(低速回線で効果あり) |
--delete | 送信元にないファイルを送信先から削除(ミラーリング) |
-e "ssh" | SSH経由で転送 |
--exclude | 指定したパターンを除外 |
--dry-run | 実際には転送せず、何が起きるかをプレビュー |
初回は必ず --dry-run で確認してください。--delete オプションを付けているため、誤ったパスを指定すると本番のファイルが消えます。
# まず dry-run で差分を確認
rsync -az --delete --dry-run \
-e "ssh" \
/path/to/local/wp-content/themes/ \
my-server:/var/www/html/wp-content/themes/
# 問題なければ --dry-run を外して実行
絶対に同期してはいけないもの
rsync でファイルを同期する際、同期対象に含めてはいけないファイル・ディレクトリがあります。これらを誤って転送すると、本番環境が動かなくなったり、セキュリティリスクが発生します。
| 対象 | 理由 |
|---|---|
wp-config.php | ローカルと本番でDB接続情報・認証キーが異なる。上書きすると本番が動かなくなる |
.htaccess | サーバー固有のリライトルールが含まれる場合がある |
wp-content/cache/ | キャッシュプラグインのローカルデータを本番に持ち込む意味がない |
wp-content/upgrade/ | アップデート用の一時ファイル |
object-cache.php | オブジェクトキャッシュのドロップインファイル。環境ごとに設定が異なる |
advanced-cache.php | ページキャッシュのドロップインファイル。同上 |
db.php | DBドロップインファイル。同上 |
debug.log | デバッグログ。ローカルのログを本番に持ち込む必要はない |
rsync の --exclude オプションで除外するか、WordPress のルートディレクトリ全体ではなく wp-content/ 配下のサブディレクトリ単位で同期するようにしましょう。
ファイル同期は wp-content/ 以下に限定するのが安全です。WordPress本体(wp-admin/、wp-includes/)やルート直下のファイルは、本番で wp core update を実行して更新するほうが確実です。
ステージング経由の安全なデプロイフロー
ローカルから直接本番にデプロイするのは手軽ですが、本番稼働中のサイトではステージング環境を挟むことをおすすめします。
推奨フロー
- ローカル → ステージング:上記のDB移行 + rsync をステージングサーバーに対して実行
- ステージングで動作確認:表示崩れ、リンク切れ、フォーム送信、プラグインの挙動をチェック
- ステージング → 本番:問題なければ、同じ手順で本番に適用
ステージング環境は、本番と同じサーバー上にサブドメイン(例:staging.example.com)で構築するのが簡単です。VPSであれば、Nginx のバーチャルホストを追加するだけで用意できます。
ロールバック手順
デプロイ後に問題が見つかった場合は、ステップ1で取得したバックアップを使って復元します。
# バックアップからDBを復元
ssh my-server "wp db import /tmp/prod-backup-20260411120000.sql --path=/var/www/html"
# キャッシュをクリア
ssh my-server "wp cache flush --path=/var/www/html"
ファイルの差し戻しが必要な場合は、Gitで管理していればチェックアウトするだけです。Gitを使っていない場合は、rsync 実行前にファイル側もバックアップを取っておくと安心です。
シェルスクリプト化のすすめ
ここまでのコマンドを毎回手打ちするのは現実的ではありません。シェルスクリプトにまとめておけば、ワンコマンドでデプロイを完了できます。
スクリプト設計のポイント
デプロイスクリプトを自作する際に押さえておくべきポイントを整理します。
- 環境変数を外部ファイルに分離:本番のホスト名、パス、URLを
.envファイルに記述し、スクリプト本体と分離する。認証情報をGitにコミットしない設計にする - 本番push時の確認プロンプト:本番にデプロイする前に「本当に実行しますか?」の確認を入れる。うっかり実行を防ぐ
- 自動バックアップ:デプロイ前に本番DBのバックアップを自動取得する処理を組み込む
- コンポーネント別の実行:DB・テーマ・プラグイン・アップロードを個別に同期できる設計にする。全部まとめて転送が不要な場面は多い
- エスケープ済みURLの置換:通常のURLだけでなく、エスケープ形式(
\/\/)の置換も自動で実行する - dry-run 対応:環境変数で
DRY_RUN=1を指定すると rsync が dry-run モードで動く仕組みにする
以下は、スクリプトの骨格にあたる部分の簡易例です。
#!/usr/bin/env bash
set -euo pipefail
# --- 設定 ---
PROD_HOST="my-server"
PROD_PATH="/var/www/html"
PROD_URL="https://example.com"
LOCAL_PATH="/path/to/local/wordpress"
LOCAL_URL="https://mysite.test"
# --- 本番push時の確認 ---
echo "[WARN] 本番環境にデプロイします。"
read -r -p "続行しますか? [y/N] " confirm
[[ "${confirm}" != "y" ]] && echo "中止しました。" && exit 1
# --- 本番DBバックアップ ---
TIMESTAMP=$(date +%Y%m%d%H%M%S)
ssh "${PROD_HOST}" "wp db export /tmp/prod-backup-${TIMESTAMP}.sql --path='${PROD_PATH}'"
# --- ローカルDBエクスポート → 本番インポート ---
wp db export /tmp/local-db.sql --path="${LOCAL_PATH}"
cat /tmp/local-db.sql | ssh "${PROD_HOST}" "wp db import - --path='${PROD_PATH}'"
# --- URL置換 ---
ssh "${PROD_HOST}" "wp search-replace '${LOCAL_URL}' '${PROD_URL}' --all-tables --precise --recurse-objects --path='${PROD_PATH}'"
ssh "${PROD_HOST}" "wp cache flush --path='${PROD_PATH}'"
ssh "${PROD_HOST}" "wp rewrite flush --path='${PROD_PATH}'"
# --- ファイル同期 ---
rsync -az --delete \
-e "ssh" \
"${LOCAL_PATH}/wp-content/themes/" \
"${PROD_HOST}:${PROD_PATH}/wp-content/themes/"
echo "デプロイ完了"
上記はあくまで骨格です。実運用では、exclude ルールの追加、エスケープ済みURLの二重置換、エラーハンドリングなどが必要になります。
Makefile で操作を簡略化
スクリプトをさらに使いやすくするなら、Makefile でラップする方法があります。make push-db、make push-theme のようにコンポーネント単位で実行できると、日常のデプロイが格段にラクになります。
# --- Makefile の例 ---
push-db:
@bash sync.sh local prod db
push-theme:
@bash sync.sh local prod theme
push-all:
@bash sync.sh local prod all
pull-db:
@bash sync.sh prod local db
make push-db でDB同期、make push-theme でテーマだけ同期、という使い分けが自然にできます。逆方向(本番 → ローカル)の pull 系も用意しておくと、本番のデータをローカルに持ってくる作業もワンコマンドで済みます。
GitHub Actions による自動デプロイの概要
さらに運用を進化させるなら、GitHub Actions でデプロイを自動化する手もあります。main ブランチへのプッシュをトリガーにして、テーマファイルを本番に自動反映する仕組みが作れます。
基本的な流れ
- ローカルでテーマを修正し、GitHubにプッシュ
- GitHub Actions がトリガーされる
- ワークフロー内で
rsyncを使い、テーマファイルをSSH経由で本番に転送 - デプロイ完了
GitHub Secrets に SSH秘密鍵・ホスト情報を登録し、ワークフローの .yml ファイルに rsync コマンドを記述します。
# .github/workflows/deploy.yml(簡易例)
name: Deploy Theme
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy via rsync
env:
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
rsync -az --delete \
-e "ssh -o StrictHostKeyChecking=no" \
./themes/my-theme/ \
${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}:/var/www/html/wp-content/themes/my-theme/
ただし、DB同期は自動化しないほうが安全です。データベースの上書きは影響範囲が大きいため、手動確認を挟むべきステップです。GitHub Actions での自動化はテーマやプラグインなどのファイル同期に留めることをおすすめします。
トラブルシューティング
URL置換後に画像が表示されない
画像のURLがローカルのままになっている可能性があります。wp search-replace を --dry-run で実行し、置換対象が残っていないか確認してください。
wp search-replace 'https://mysite.test' 'https://example.com' --all-tables --dry-run --path=/var/www/html
置換件数が0件でなければ、再度実行して漏れを解消します。エスケープ済みURLの置換を忘れているケースもよくあります。
管理画面にログインできない
ローカルのユーザー情報で上書きされたため、本番のパスワードが変わっている可能性があります。WP-CLI でパスワードをリセットできます。
ssh my-server "wp user update admin --user_pass='new-password' --path=/var/www/html"
rsync で Permission denied が出る
SSHの鍵認証が正しく設定されているか、~/.ssh/config のホスト名やポート番号を確認してください。また、本番サーバー側のファイルの所有者とrsyncの実行ユーザーが一致しているかもチェックします。KUSANAGI環境では、ファイルの所有者が kusanagi ユーザーであることが多いため、rsyncもそのユーザーで接続する必要があります。
パーマリンクが機能しない(404エラー)
wp rewrite flush を実行してパーマリンクを再生成してください。それでも解決しない場合は、Nginx の設定で try_files ディレクティブが正しく設定されているか確認します。Apache の場合は .htaccess の RewriteRule を確認してください。
よくある質問
- 本番を触らずにテストするにはどうすればいいですか?
ステージング環境を作るのがベストです。本番と同じサーバー上にサブドメインで構築すれば、同じインフラ条件でテストできます。
wp search-replaceでURLをステージング用に置換すれば、独立した検証環境が完成します。- プラグインのライセンスはどうなりますか?
多くの有料プラグインはドメインベースでライセンスを管理しています。ローカルからデプロイした場合でも、本番ドメインでライセンス認証されていれば問題ありません。ステージング用のドメインについては、プラグインの提供元に確認してください。
- 本番のデータをローカルに持ってくることは可能ですか?
可能です。この記事で紹介した手順の方向を逆にするだけです。本番でDBエクスポート → ローカルにインポート → URL置換(本番URL → ローカルURL)、rsyncも本番 → ローカルの方向で実行します。
- マルチサイト環境でも使えますか?
wp search-replaceはマルチサイトにも対応しています。ただし、--urlオプションでサイトを指定する必要があるなど注意点が増えます。マルチサイトの場合は事前にテスト環境で十分に検証してください。
まとめ
WP-CLI の wp db コマンドと rsync を使えば、プラグインに頼らず、再現性の高いデプロイが実現できます。
- DB移行:
wp db export→wp db import→wp search-replace - ファイル同期:rsync で差分転送。
--dry-runで必ず事前確認 - 安全策:デプロイ前の本番バックアップは必須。ステージングを挟むとさらに安全
- 効率化:シェルスクリプト化 → Makefile で操作を簡略化 → GitHub Actions でテーマの自動デプロイ
一見するとコマンド数が多く感じますが、一度スクリプト化してしまえば、以降は make push-all のようなワンコマンドで完了します。プラグインの制限に悩んでいた方は、ぜひこの方法を試してみてください。
なお、ローカル開発環境の構築がまだの方は、WordPressローカル開発環境の最適解で各ツールの比較を、Herd + DBnginでWordPressローカル環境を5分で構築で具体的なセットアップ手順をまとめています。テーマのHMR開発に興味がある方はViteでWordPressテーマのHMRを実現するもあわせてご覧ください。


Comment