1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

改ざんされても30分で戻せるWordPressバックアップ&復元手順(wp-cli/cron・コピペOK)

1
Posted at

WordPress を運用していて「バックアップは取っているはず」と思っていても、いざ改ざん・障害が起きたときに 本当に戻せるか を試したことがある人は多くありません。

この記事は、自分(または制作会社)でサーバを管理している Web 担当者 向けに、WordPress を「DB とファイルの両方を、世代を残して、別環境へ実際に復元できる状態」まで持っていく手順を、wp-clicron のコピペ設定でまとめたものです。バックアップの取得だけでなく、「戻せることを確認する復元演習」までを 1 セット として扱います。

対象環境は Linux サーバ + WordPress(wp-cli が使えること)です。

なぜ「バックアップを取っている」だけでは危ないのか

改ざんや誤操作からの復旧でよくある失敗は、バックアップの有無ではなく 「復元を試したことがない」 ことに起因します。

  • DB だけ取れていて wp-content/uploads(画像)やプラグインのファイルが抜けていた
  • バックアップファイルは残っていたが、サイズが 0 バイトで中身が空だった(cron のパスミス)
  • 世代が 1 つしかなく、改ざんに気づいた時点で 改ざん後の状態で上書き されていた
  • 同じサーバの同じディスクにしか置いておらず、サーバごと飛んだら一緒に消えた

つまり守るべきは「取得」ではなく 「任意の時点に、別の場所から、確実に戻せる」 という状態です。以下ではこの状態を作ります。

何をバックアップするか(2 系統)

WordPress の実体は大きく 2 つに分かれます。両方揃って初めて復元できます。

対象 中身 取得方法
データベース 記事・設定・ユーザー wp db export(mysqldump)
ファイル テーマ・プラグイン・アップロード画像・wp-config.php tarwp-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 回スケジュールに入れる ことが、この記事で一番伝えたいポイントです。

改ざんに気づいたときの復旧フロー

実際に改ざんされた場合は、いきなり上書きせず順番を守ります。

  1. 切り離す: 一時的にメンテナンス状態にし、被害範囲の証跡(改ざん日時・不審ファイル)を控える
  2. 改ざん前の世代を選ぶ: 最新ではなく、異常が入る前 の世代を選ぶ(世代管理が効くのはここ)
  3. 別ディレクトリに復元して正常性を確認してから、本番へ切り替える
  4. 侵入経路を塞ぐ: 復元しただけでは同じ穴から再度やられます。パスワード変更・プラグイン更新・不要アカウント削除まで行う

改ざんは「入口」を塞がないと再発します。ログイン保護やプラグイン更新運用とセットで考えてください。

まとめ

  • バックアップは DB + ファイルの 2 系統 を揃える
  • 世代を残す(改ざんは気づくのが遅れるため最新だけでは足りない)
  • オフサイト に複製する(同一ディスクは一緒に失われる)
  • そして 復元演習 で「本当に戻せる」ことを月 1 回確認する

「取得しているか」ではなく「戻せるか」を基準にすると、運用の抜けが見えてきます。

関連記事

バックアップ体制と合わせて、公開サーバ側の設定に穴がないかは無料・登録不要のサイトドックで自動チェックできます → https://sitedock.jp

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?