2026年10月20日

2026年10月20日

WordPressのバックアップエラーを解決する方法

はじめに

WordPressのバックアッププラグイン(UpdraftPlus・BackWPupなど)を実行すると途中でエラーが発生して完了しない・バックアップファイルは作成されるが復元しようとすると「ファイルが壊れています」エラーになる・スケジュールされた自動バックアップが実行されていない・バックアップが完了するが肝心なデータベースが含まれていないといった問題は、PHPのタイムアウト・メモリ不足・ファイル権限・wp-cronの停止が原因です。

症状・原因

  • バックアップが50%前後で止まり「タイムアウト」エラーが表示される
  • バックアップファイルは作成されるが解凍すると中身が不完全
  • 毎日バックアップを設定したが数日後に確認すると実行されていない
  • バックアップには含まれているがデータベースの.sqlファイルが0バイト

解決手順

ステップ1:バックアップの状態を診断する

# WP-CLIでデータベースをエクスポート(最も信頼性が高い方法)
wp db export backup-$(date +%Y%m%d-%H%M%S).sql --add-drop-table

# バックアップファイルの確認
wp db export - | wc -c   # 標準出力にエクスポートしてサイズ確認

# データベースのサイズを確認
wp eval "
global \$wpdb;
\$size = \$wpdb->get_var('SELECT SUM(data_length + index_length) / 1024 / 1024 FROM information_schema.tables WHERE table_schema = DATABASE()');
echo 'DB size: ' . number_format((float) \$size, 2) . ' MB' . PHP_EOL;
"

# バックアップに関連するCronジョブを確認
wp cron event list --format=table | grep -i backup
wp cron test
# PHP設定を確認
wp eval "
echo 'memory_limit: '       . ini_get('memory_limit') . PHP_EOL;
echo 'max_execution_time: ' . ini_get('max_execution_time') . PHP_EOL;
echo 'upload_max_filesize: ' . ini_get('upload_max_filesize') . PHP_EOL;
echo 'post_max_size: '      . ini_get('post_max_size') . PHP_EOL;
"

ステップ2:WP-CLIで完全バックアップを実行する

# データベースバックアップ
wp db export /var/backups/wordpress/db-$(date +%Y%m%d).sql \
    --add-drop-table \
    --single-transaction \
    --quick

# ファイルバックアップ(wp-content全体)
tar -czf /var/backups/wordpress/files-$(date +%Y%m%d).tar.gz \
    --exclude='wp-content/cache' \
    --exclude='wp-content/upgrade' \
    --exclude='wp-content/uploads/backups' \
    wp-content/

# バックアップの整合性確認
gzip -t /var/backups/wordpress/files-$(date +%Y%m%d).tar.gz && echo "OK" || echo "CORRUPTED"

# 古いバックアップを自動削除(30日以上前のファイル)
find /var/backups/wordpress/ -name "*.sql" -mtime +30 -delete
find /var/backups/wordpress/ -name "*.tar.gz" -mtime +30 -delete

ステップ3:自動バックアップをCronで設定する

# サーバーのcrontabに追加(毎日深夜2時にバックアップ)
crontab -e
# 以下を追加:
# 0 2 * * * /usr/local/bin/wp db export /var/backups/wp-db-$(date +\%Y\%m\%d).sql --path=/var/www/html --allow-root
# 0 3 * * 0 tar -czf /var/backups/wp-files-$(date +\%Y\%m\%d).tar.gz -C /var/www/html wp-content/
// functions.php: WordPress cronを使った自動バックアップ(軽量版)

// ① カスタムcronスケジュールを追加
add_filter('cron_schedules', function(array $schedules): array {
    $schedules['weekly'] = [
        'interval' => WEEK_IN_SECONDS,
        'display'  => '毎週',
    ];
    return $schedules;
});

// ② バックアップcronを登録
add_action('init', function(): void {
    if (!wp_next_scheduled('wp_auto_backup')) {
        wp_schedule_event(strtotime('tomorrow 02:00:00'), 'daily', 'wp_auto_backup');
    }
});

// ③ バックアップ処理
add_action('wp_auto_backup', function(): void {
    global $wpdb;

    $backup_dir = WP_CONTENT_DIR . '/backups/';
    if (!is_dir($backup_dir)) {
        wp_mkdir_p($backup_dir);
        // バックアップディレクトリへの直接アクセスを禁止
        file_put_contents($backup_dir . '.htaccess', 'Deny from all');
    }

    // データベースを小分けでバックアップ
    $tables  = $wpdb->get_col('SHOW TABLES');
    $sql     = '';
    $date    = date('Y-m-d-His');

    foreach ($tables as $table) {
        $create = $wpdb->get_row("SHOW CREATE TABLE `{$table}`", ARRAY_N);
        $sql   .= "\n\n" . $create[1] . ";\n\n";

        $rows = $wpdb->get_results("SELECT * FROM `{$table}`", ARRAY_A);
        foreach ($rows as $row) {
            $values = array_map(function($v) use ($wpdb) {
                return $v === null ? 'NULL' : "'" . $wpdb->_escape($v) . "'";
            }, $row);
            $sql .= "INSERT INTO `{$table}` VALUES (" . implode(', ', $values) . ");\n";
        }
    }

    $filename = $backup_dir . "db-backup-{$date}.sql";
    file_put_contents($filename, $sql);
    error_log(sprintf('[Backup] DB backup created: %s (%s bytes)', basename($filename), filesize($filename)));

    // 古いバックアップを削除(5世代保持)
    $backups = glob($backup_dir . 'db-backup-*.sql');
    if (count($backups) > 5) {
        sort($backups);
        array_splice($backups, -5);
        foreach ($backups as $old) {
            unlink($old);
        }
    }
});

ステップ4:バックアップからの復元手順

# データベースの復元
wp db import /var/backups/wordpress/db-20240115.sql

# 特定のテーブルのみ復元
mysql -u dbuser -p dbname < /var/backups/wordpress/db-20240115.sql

# ファイルの復元
tar -xzf /var/backups/wordpress/files-20240115.tar.gz -C /var/www/html/

# 復元後のURL修正(ドメインが変わった場合)
wp search-replace 'https://old-domain.com' 'https://new-domain.com' --all-tables
wp cache flush
wp rewrite flush

# 復元後の動作確認
wp core verify-checksums
wp plugin verify-checksums --all
// functions.php: 復元後の自動修正処理
add_action('admin_init', function(): void {
    // バックアップからの復元後フラグを確認
    if (get_option('_backup_restored')) {
        // キャッシュをクリア
        wp_cache_flush();

        // パーマリンクを再生成
        flush_rewrite_rules(true);

        // フラグを削除
        delete_option('_backup_restored');

        error_log('[Backup] Post-restore cleanup completed');
    }
});

ステップ5:バックアップ戦略を最適化する

// functions.php: バックアップ通知と監視

// ① バックアップ完了通知
add_action('wp_auto_backup', function(): void {
    // バックアップ後にメール通知
    $backup_dir  = WP_CONTENT_DIR . '/backups/';
    $files       = glob($backup_dir . 'db-backup-*.sql');
    $latest_file = $files ? end($files) : null;

    if ($latest_file) {
        $size = round(filesize($latest_file) / 1024 / 1024, 2);
        wp_mail(
            get_option('admin_email'),
            sprintf('[%s] 自動バックアップ完了', get_bloginfo('name')),
            sprintf(
                "バックアップが正常に完了しました。\n\nファイル: %s\nサイズ: %s MB\n日時: %s",
                basename($latest_file),
                $size,
                current_time('Y-m-d H:i:s')
            )
        );
    }
}, 20);  // バックアップ処理(priority=10)の後に実行

// ② バックアップの健全性を定期チェック
add_action('wp_daily_backup_check', function(): void {
    $backup_dir = WP_CONTENT_DIR . '/backups/';
    $files      = glob($backup_dir . 'db-backup-*.sql') ?: [];

    if (empty($files)) {
        wp_mail(
            get_option('admin_email'),
            sprintf('[%s] 警告:バックアップが見つかりません', get_bloginfo('name')),
            'バックアップファイルが存在しません。バックアップ設定を確認してください。'
        );
        return;
    }

    $latest   = end($files);
    $modified = filemtime($latest);
    $days_ago = (time() - $modified) / DAY_IN_SECONDS;

    if ($days_ago > 2) {
        wp_mail(
            get_option('admin_email'),
            sprintf('[%s] 警告:バックアップが古くなっています', get_bloginfo('name')),
            sprintf('最後のバックアップは %d 日前です。バックアップの設定を確認してください。', (int) $days_ago)
        );
    }
});

注意事項

  • WordPressのwp-cronを使ったバックアップはリクエストベースのため、サイトへのアクセスがない時間帯には実行されません。本番環境ではサーバーのcrontabでWP-CLIを使ったバックアップを設定することを強くお勧めします
  • バックアップファイルをwp-content/backups/に保存する場合は.htaccessでアクセス制限を必ず設定してください。データベースダンプにはパスワード等の機密情報が含まれています
  • 大規模サイト(DBが1GB超)の場合、PHPによるバックアップは現実的でありません。mysqldumpをサーバーのcronで直接実行するか、RDSなどのマネージドDBサービスのスナップショット機能を活用してください

まとめ

WordPressバックアップエラーの解決は①wp db export backup.sqlでWP-CLIからDB直接エクスポート・wp cron testでwp-cron動作確認・DBサイズとPHPのメモリ/タイムアウト設定の確認、②wp db export --single-transaction --quickで整合性のあるDB書き出し・tar -czfでwp-contentを圧縮・gzip -tでファイル整合性確認、③サーバーcrontabにwp db exportコマンドを追加・wp_schedule_eventでWordPress cron登録・バックアップディレクトリに.htaccessでアクセス制限、④wp db importでDB復元・wp search-replaceでURL修正・wp core verify-checksumsで整合性確認、⑤バックアップ完了メール通知・2日以上古いバックアップの警告・5世代ローテーションの自動管理の手順で解決します。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲