WordPress を運用していて「バックアップは取っているはず」と思っていても、いざ改ざん・障害が起きたときに 本当に戻せるか を試したことがある人は多くありません。
この記事は、自分(または制作会社)でサーバを管理している Web 担当者 向けに、WordPress を「DB とファイルの両方を、世代を残して、別環境へ実際に復元できる状態」まで持っていく手順を、wp-cli と cron のコピペ設定でまとめたものです。バックアップの取得だけでなく、「戻せることを確認する復元演習」までを 1 セット として扱います。
対象環境は Linux サーバ + WordPress(wp-cli が使えること)です。
なぜ「バックアップを取っている」だけでは危ないのか
改ざんや誤操作からの復旧でよくある失敗は、バックアップの有無ではなく 「復元を試したことがない」 ことに起因します。
- DB だけ取れていて
wp-content/uploads(画像)やプラグインのファイルが抜けていた - バックアップファイルは残っていたが、サイズが 0 バイトで中身が空だった(cron のパスミス)
- 世代が 1 つしかなく、改ざんに気づいた時点で 改ざん後の状態で上書き されていた
- 同じサーバの同じディスクにしか置いておらず、サーバごと飛んだら一緒に消えた
つまり守るべきは「取得」ではなく 「任意の時点に、別の場所から、確実に戻せる」 という状態です。以下ではこの状態を作ります。
何をバックアップするか(2 系統)
WordPress の実体は大きく 2 つに分かれます。両方揃って初めて復元できます。
| 対象 | 中身 | 取得方法 |
|---|---|---|
| データベース | 記事・設定・ユーザー |
wp db export(mysqldump) |
| ファイル | テーマ・プラグイン・アップロード画像・wp-config.php
|
tar で wp-content と設定ファイル |
Step 1: 手動で 1 回、確実に取得する
まずは自動化の前に手で 1 回通します。WP_PATH は自分の WordPress ルートに書き換えてください。
#!/usr/bin/env bash
set -euo pipefail
WP_PATH="/var/www/example.com/public"
DEST="/var/backups/wp/example.com"
STAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$DEST"
# 1) データベース(--single-transaction でロックせずに整合性を保つ)
wp db export "$DEST/db-$STAMP.sql" \
--path="$WP_PATH" \
--single-transaction --quick --add-drop-table
# 2) ファイル(uploads・テーマ・プラグイン・wp-config.php)
tar czf "$DEST/files-$STAMP.tar.gz" \
-C "$WP_PATH" wp-content wp-config.php
# 3) 取得直後にサイズを確認(0 バイトは失敗)
ls -lh "$DEST"/db-"$STAMP".sql "$DEST"/files-"$STAMP".tar.gz
--single-transaction を付けると InnoDB のテーブルをロックせずに一貫性のあるダンプが取れるため、稼働中でも実行できます。取得したら必ずサイズを確認 してください(0 バイト・数十バイトは失敗のサインです)。
Step 2: 世代管理付きスクリプトにする
改ざんは気づくまでに時間がかかることがあるため、世代(履歴)を残す ことが重要です。ここでは 7 世代残し、古いものから自動削除します。
#!/usr/bin/env bash
# /usr/local/bin/wp-backup.sh
set -euo pipefail
WP_PATH="/var/www/example.com/public"
DEST="/var/backups/wp/example.com"
KEEP=7 # 残す世代数
STAMP="$(date +%Y%m%d-%H%M%S)"
LOG="/var/log/wp-backup.log"
mkdir -p "$DEST"
{
echo "[$(date)] backup start"
wp db export "$DEST/db-$STAMP.sql" \
--path="$WP_PATH" --single-transaction --quick --add-drop-table
tar czf "$DEST/files-$STAMP.tar.gz" \
-C "$WP_PATH" wp-content wp-config.php
# 取得物が空でないかチェック(異常なら終了コード 1 で失敗させる)
if [ ! -s "$DEST/db-$STAMP.sql" ] || [ ! -s "$DEST/files-$STAMP.tar.gz" ]; then
echo "[$(date)] ERROR: empty backup file" ; exit 1
fi
# 世代整理: 新しい順に KEEP 個を残して残りを削除
ls -1t "$DEST"/db-*.sql | tail -n +$((KEEP + 1)) | xargs -r rm -f
ls -1t "$DEST"/files-*.tar.gz | tail -n +$((KEEP + 1)) | xargs -r rm -f
echo "[$(date)] backup done"
} >> "$LOG" 2>&1
実行権限を付けます。
sudo chmod 700 /usr/local/bin/wp-backup.sh
sudo chown root:root /usr/local/bin/wp-backup.sh
バックアップには wp-config.php(DB 接続情報)が含まれるため、保存先ディレクトリのパーミッションは 700 に絞り、Web 公開ディレクトリの外に置きます。
Step 3: cron で自動化する
毎日深夜 3 時に実行する例です(root の crontab)。
# /etc/cron.d/wp-backup
# 分 時 日 月 曜 ユーザー コマンド
0 3 * * * root /usr/local/bin/wp-backup.sh
cron はメール通知が飛ばないと失敗に気づけません。ログ(/var/log/wp-backup.log)を週次で見る か、監視サービスに「最新ファイルの更新時刻」を見張らせると安全です。
Step 4: オフサイト(別の場所)へ複製する
同じサーバに置いたバックアップは、サーバごと侵害・故障すると一緒に失われます。別ストレージへ 1 日 1 回同期します(rsync over SSH の例。鍵認証を前提)。
# バックアップ用サーバへ差分同期(--delete は付けない=誤削除の巻き添え回避)
rsync -az --partial \
/var/backups/wp/example.com/ \
backup@backup-host:/srv/wp-backups/example.com/
オフサイト側では 書き込み専用アカウント にし、万一 Web サーバが乗っ取られてもバックアップ先を消させない構成が理想です。
Step 5: いちばん大事な「復元演習」
バックアップは 戻せて初めて価値 があります。本番とは別のディレクトリ(またはステージング)に、直近のバックアップから復元してみます。
#!/usr/bin/env bash
set -euo pipefail
SRC="/var/backups/wp/example.com"
RESTORE="/var/www/restore-test" # 本番とは別の空ディレクトリ
DB_LATEST="$(ls -1t "$SRC"/db-*.sql | head -1)"
FILES_LATEST="$(ls -1t "$SRC"/files-*.tar.gz | head -1)"
mkdir -p "$RESTORE"
# 1) ファイルを展開
tar xzf "$FILES_LATEST" -C "$RESTORE"
# 2) 検証用の空 DB を作り、ダンプを流し込む
# (本番 DB とは別名にすること。本番を絶対に上書きしない)
wp db create --path="$RESTORE" || true
wp db import "$DB_LATEST" --path="$RESTORE"
# 3) 検証用ドメインでアクセスできるよう URL を一時置換
wp search-replace 'https://example.com' 'https://restore-test.example.com' \
--path="$RESTORE" --all-tables --skip-columns=guid
演習では次を必ず確認します。
- トップページと管理画面(
wp-admin)が開くか - 画像(
wp-content/uploads)が表示されるか - 記事数・ユーザー数が本番と一致するか(
wp post list/wp user list)
ここまで通れば「戻せる」ことが実証できます。この演習を月 1 回スケジュールに入れる ことが、この記事で一番伝えたいポイントです。
改ざんに気づいたときの復旧フロー
実際に改ざんされた場合は、いきなり上書きせず順番を守ります。
- 切り離す: 一時的にメンテナンス状態にし、被害範囲の証跡(改ざん日時・不審ファイル)を控える
- 改ざん前の世代を選ぶ: 最新ではなく、異常が入る前 の世代を選ぶ(世代管理が効くのはここ)
- 別ディレクトリに復元して正常性を確認してから、本番へ切り替える
- 侵入経路を塞ぐ: 復元しただけでは同じ穴から再度やられます。パスワード変更・プラグイン更新・不要アカウント削除まで行う
改ざんは「入口」を塞がないと再発します。ログイン保護やプラグイン更新運用とセットで考えてください。
まとめ
- バックアップは DB + ファイルの 2 系統 を揃える
- 世代を残す(改ざんは気づくのが遅れるため最新だけでは足りない)
- オフサイト に複製する(同一ディスクは一緒に失われる)
- そして 復元演習 で「本当に戻せる」ことを月 1 回確認する
「取得しているか」ではなく「戻せるか」を基準にすると、運用の抜けが見えてきます。
関連記事
バックアップ体制と合わせて、公開サーバ側の設定に穴がないかは無料・登録不要のサイトドックで自動チェックできます → https://sitedock.jp