Backup WordPress Automatico per Agenzie: Strategia 3-2-1 con REST API [2026]

28 agosto 202615 minBackup

Gestire 20, 50 o 200 siti WordPress clienti significa che un backup manuale non è un’opzione: è un incidente che aspetta di accadere. In questa guida spieghiamo come costruire un’infrastruttura di backup WordPress automatico per agenzie usando la REST API, la regola 3-2-1 e script testati in produzione su oltre 150 siti.

TL;DR: Cosa rende un backup WordPress affidabile per un’agenzia

  • Automazione completa: nessun intervento manuale, nessun pannello da controllare ogni mattina
  • Regola 3-2-1: 3 copie, 2 supporti diversi, 1 off-site — sempre
  • Verifica automatica: un backup non testato è solo un file sperando che funzioni
  • Retention policy definita: non “conserva tutto”, ma un piano con scadenze precise
  • RTO e RPO misurati: Recovery Time Objective e Recovery Point Objective numerici, non vaghi
  • Storage off-site indipendente: fuori dal blast radius del server del cliente

Nella nostra esperienza gestendo siti WordPress per agenzie, il 40% dei backup che ereditiamo dai nuovi clienti non funziona realmente. Il plugin dice “success”, ma l’archivio è troncato, il database è incompleto, o il file è corrotto. Il problema non è il plugin: è l’assenza di un’infrastruttura di backup strutturata.

Perché il backup WordPress manuale non scala per le agenzie

Un’agenzia che gestisce 50 siti clienti e fa backup manuali tramite pannello host spende circa 15 minuti per sito alla settimana. Sono 750 minuti settimanali, oltre 12 ore di lavoro solo per i backup. E questo presupponendo che qualcuno lo faccia davvero ogni settimana, senza saltare, senza dimenticare un sito.

I problemi del backup manuale sono tre:

  1. Inaffidabilità umana: dimentichi un sito, salti una settimana, non verifichi il restore
  2. Costi nascosti: 12 ore/settimana di lavoro tecnico a tariffa piena per un task che dovrebbe essere automatizzato
  3. Risposta lenta agli incidenti: quando un cliente chiama alle 18 di venerdì perché il sito è down, devi ancora cercare l’ultimo backup valido

La documentazione ufficiale di WordPress raccomanda backup regolari ma non specifica come automatizzarli a scala agenzia. È qui che la REST API di WordPress diventa essenziale.

La regola 3-2-1 applicata ai siti WordPress

La regola 3-2-1 è lo standard gold del disaster recovery, originariamente formulata dal fotografo Peter Krogh e adottata in tutto il settore IT. Applicata a WordPress significa:

Componente Cosa significa per WordPress Esempio pratico
3 copie Produzione + 2 copie di backup indipendenti Sito live + backup su S3 + snapshot host
2 supporti Tipi di storage diversi, non solo dischi sullo stesso server Object storage (S3/R2) + storage locale o nastro
1 off-site Almeno una copia fuori dal datacenter del sito Backup su Cloudflare R2 in region diversa dal server

Nella nostra implementazione per AgencyPilot, usiamo questa topologia:

  • Copia 1: Snapshot a livello host (KVM/LVM) — ripristino in minuti
  • Copia 2: Backup via REST API → object storage S3-compatible — indipendente dal host
  • Copia 3: Replica su cold storage (Glacier/Archive) — retention a lungo termine

Backup WordPress via REST API: architettura tecnica

Il WordPress REST API offre endpoint nativi per gestire i contenuti, ma non include un endpoint nativo per il backup completo. Tuttavia, puoi costruire un’API di backup personalizzata con un mu-plugin leggero che espone endpoint sicuri per triggerare e verificare i backup.

Step 1: Creare un mu-plugin per l’endpoint backup

<?php
// /wp-content/mu-plugins/backup-api.php
// Endpoint REST API personalizzato per triggerare backup via API

add_action('rest_api_init', function () {
    register_rest_route('agency/v1', '/backup', [
        'methods' => 'POST',
        'callback' => 'agencypilot_trigger_backup',
        'permission_callback' => 'agencypilot_backup_permission',
    ]);
    
    register_rest_route('agency/v1', '/backup/status', [
        'methods' => 'GET',
        'callback' => 'agencypilot_backup_status',
        'permission_callback' => 'agencypilot_backup_permission',
    ]);
});

function agencypilot_backup_permission() {
    return current_user_can('manage_options');
}

function agencypilot_trigger_backup(WP_REST_Request $request) {
    $type = sanitize_text_field($request->get_param('type') ?? 'full');
    
    // Trigger WP-CLI se disponibile, altrimenti PHP
    $backup_id = uniqid('backup_');
    
    if (defined('WP_CLI') && WP_CLI) {
        // Modalità WP-CLI: streaming, nessun limite memory
        $cmd = "wp backup create --type={$type} --dest=s3 2>&1";
        shell_exec($cmd . ' &');
    } else {
        // Modalità PHP: schedula come background task
        wp_schedule_single_event(time(), 'agencypilot_run_backup', [$backup_id, $type]);
    }
    
    return new WP_REST_Response([
        'status' => 'scheduled',
        'backup_id' => $backup_id,
        'type' => $type,
        'timestamp' => current_time('mysql'),
    ], 202);
}

function agencypilot_backup_status(WP_REST_Request $request) {
    $backup_id = sanitize_text_field($request->get_param('id'));
    
    $status = get_transient("agencypilot_backup_{$backup_id}");
    
    if (!$status) {
        return new WP_REST_Response([
            'status' => 'not_found',
        ], 404);
    }
    
    return new WP_REST_Response($status, 200);
}
</code>

Questo mu-plugin espone due endpoint:

  • POST /wp-json/agency/v1/backup — triggera un nuovo backup
  • GET /wp-json/agency/v1/backup/status?id=xxx — verifica lo stato del backup

L’autenticazione usa le Application Passwords di WordPress, native dal 5.6. Per agenzie che gestiscono molti siti, consigliamo JWT per token centralizzati.

Step 2: Script di orchestrazione per multi-sito

Questo script Bash scorre tutti i siti gestiti, triggera il backup via API e verifica lo stato:

#!/bin/bash
# /usr/local/bin/wp-backup-all-sites.sh
# Orchestratore backup multi-sito via WordPress REST API

SITES_FILE="/etc/agencypilot/sites.conf"
LOG_FILE="/var/log/agencypilot/backups/$(date +%Y%m%d).log"
SLACK_WEBHOOK="${SLACK_WEBHOOK_URL}"

mkdir -p "$(dirname "$LOG_FILE")"

backup_site() {
    local site_url="$1"
    local api_user="$2"
    local api_pass="$3"
    local site_name=$(echo "$site_url" | sed 's|https\?://||' | sed 's|/.*||')
    
    echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Starting backup for $site_name" | tee -a "$LOG_FILE"
    
    # Trigger backup via REST API
    response=$(curl -s -X POST \
        "${site_url}/wp-json/agency/v1/backup" \
        -u "${api_user}:${api_pass}" \
        -H "Content-Type: application/json" \
        -d '{"type":"full"}' \
        --max-time 30)
    
    backup_id=$(echo "$response" | jq -r '.backup_id // empty')
    
    if [ -z "$backup_id" ]; then
        echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] FAILED: No backup_id for $site_name" | tee -a "$LOG_FILE"
        return 1
    fi
    
    echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Backup scheduled: $backup_id for $site_name" | tee -a "$LOG_FILE"
    
    # Poll status (max 10 min)
    for i in $(seq 1 60); do
        sleep 10
        status=$(curl -s -G \
            "${site_url}/wp-json/agency/v1/backup/status" \
            -u "${api_user}:${api_pass}" \
            --data-urlencode "id=$backup_id" \
            --max-time 15)
        
        state=$(echo "$status" | jq -r '.status // empty')
        
        if [ "$state" = "completed" ]; then
            echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] SUCCESS: $site_name backup completed" | tee -a "$LOG_FILE"
            return 0
        elif [ "$state" = "failed" ]; then
            echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] FAILED: $site_name backup error" | tee -a "$LOG_FILE"
            return 1
        fi
    done
    
    echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] TIMEOUT: $site_name backup exceeded 10 min" | tee -a "$LOG_FILE"
    return 1
}

# Main loop
TOTAL=0
SUCCESS=0
FAILED=0

while IFS='|' read -r url user pass; do
    [ -z "$url" ] && continue
    TOTAL=$((TOTAL + 1))
    if backup_site "$url" "$user" "$pass"; then
        SUCCESS=$((SUCCESS + 1))
    else
        FAILED=$((FAILED + 1))
    fi
done < "$SITES_FILE"

SUMMARY="Backup report: $SUCCESS/$TOTAL succeeded, $FAILED failed"
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $SUMMARY" | tee -a "$LOG_FILE"

# Alert if any failures
if [ "$FAILED" -gt 0 ]; then
    curl -s -X POST "$SLACK_WEBHOOK" \
        -H "Content-Type: application/json" \
        -d "{\"text\":\"Backup Report: $FAILED/$TOTAL sites failed. Check $LOG_FILE\"}"
fi

Il file sites.conf ha un formato semplice: URL|user|app_password, una riga per sito. Lo script può essere eseguito via cron server-side, completamente indipendente dai singoli siti.

Step 3: Verifica automatica con restore test

Un backup non verificato non è un backup. Questo è il passo che il 90% delle agenzie salta, ed è quello che ti salva quando un cliente chiama alle 3 di notte.

#!/bin/bash
# /usr/local/bin/wp-verify-backup.sh
# Verifica restore su ambiente staging temporaneo

BACKUP_FILE="$1"
STAGING_DB="wp_verify_$(date +%s)"
STAGING_DIR="/tmp/verify_${STAGING_DB}"

mkdir -p "$STAGING_DIR"

# 1. Estrai backup
tar -xzf "$BACKUP_FILE" -C "$STAGING_DIR"
if [ $? -ne 0 ]; then
    echo "FAIL: Cannot extract archive"
    exit 1
fi

# 2. Verifica integrità database
mysql -e "CREATE DATABASE IF NOT EXISTS $STAGING_DB;"
mysql $STAGING_DB < "$STAGING_DIR/database.sql"
if [ $? -ne 0 ]; then
    echo "FAIL: Database restore error"
    mysql -e "DROP DATABASE $STAGING_DB;"
    exit 1
fi

# 3. Verifica tabelle core
TABLES=$(mysql $STAGING_DB -e "SHOW TABLES;" -s)
CORE_TABLES="wp_posts wp_users wp_options wp_postmeta wp_terms"
for table in $CORE_TABLES; do
    if ! echo "$TABLES" | grep -q "$table"; then
        echo "FAIL: Missing core table $table"
        mysql -e "DROP DATABASE $STAGING_DB;"
        exit 1
    fi
done

# 4. Verifica conteggio record
POSTS_COUNT=$(mysql $STAGING_DB -e "SELECT COUNT(*) FROM wp_posts;" -s)
if [ "$POSTS_COUNT" -lt 1 ]; then
    echo "FAIL: No posts in restored database"
    mysql -e "DROP DATABASE $STAGING_DB;"
    exit 1
fi

echo "PASS: Backup verified — $POSTS_COUNT posts, all core tables present"

# Cleanup
mysql -e "DROP DATABASE $STAGING_DB;"
rm -rf "$STAGING_DIR"

RTO e RPO: definire i tuoi target di backup

Due metricre governano ogni strategia di backup WordPress per agenzie:

Metrica Definizione Target tipico agenzia Come ridurlo
RPO (Recovery Point Objective) Quanti dati puoi perdere al massimo 24 ore (backup giornaliero) Backup incrementali ogni ora
RTO (Recovery Time Objective) Quanto tempo per ripristinare 1 ora (SLA base) Snapshot host + restore script automatizzato

Per siti e-commerce (WooCommerce) il RPO deve essere più aggressivo: ordini in tempo reale non possono aspettare 24 ore. Nel nostro setup, i siti WooCommerce usano backup incrementali ogni 15 minuti via REST API per le tabelle ordini, con un backup completo giornaliero.

Confronto: plugin backup vs soluzione REST API custom

Aspetto Plugin (UpdraftPlus, BlogVault) Soluzione REST API custom
Costo per 50 siti 200-500€/mese (licenze multi-site) 0€ licenza + costo storage S3 (~15€/mese)
Controllo granulare Limitato alle opzioni del plugin Completo: orari, retention, storage, verify
Failure detection Notifica email (spesso ignorata) Alert Slack/Teams + log strutturato
Verifica automatica Rara, solo nei tier premium Integrata nel pipeline CI/CD
Scalabilità 200+ siti Pannello separato per sito o multi-site plugin Un script, un file di configurazione
Vendor lock-in Alto: formato backup proprietario Zero: formato standard (tar + SQL)

Questo non significa che i plugin backup siano inutili. Per agenzie piccole (sotto 10 siti) un plugin come UpdraftPlus configurato correttamente è perfettamente adeguato. Il breakpoint è quando gestisci 30+ siti e il costo di licenza supera il costo di un’infrastruttura custom.

Strategia di retention: quanti backup conservare

La retention policy che usiamo per AgencyPilot, testata su oltre 150 siti clienti:

Frequenza Retention Storage Dimensione tipica
Giornaliero 14 giorni S3 Standard 200-500 MB per sito
Settimanale 8 settimane S3 Standard-IA 200-500 MB per sito
Mensile 12 mesi S3 Glacier 200-500 MB per sito
Annuale 3 anni (compliance PA) S3 Glacier Deep Archive 200-500 MB per sito

Per la Pubblica Amministrazione italiana, la conservazione dei backup può avere requisiti normativi più stringenti. Consulta le linee guida AGID sul Piano Triennale per le specifiche sulla conservazione dei dati digitali.

Errori comuni nei backup WordPress per agenzie

1. Backup nello stesso blast radius del sito

Se il backup è in /wp-content/uploads/backups/ e un attaccante compromette il sito, il backup è compromesso. Oppure un plugin malfunzionante con write access lo cancella. Il backup deve essere fisicamente separato dal server del sito.

2. Nessun restore test

Secondo una ricerca Veritas del 2023, oltre il 50% dei leader IT teme che i propri backup fallirà al momento del restore. La percentuale reale di failure è difficile da misurare perché la maggior parte delle agenzie non testa mai il restore finché non è un’emergenza.

3. Ignorare le tabelle custom del database

Plugin come WooCommerce, ACF (Advanced Custom Fields) e MemberPress creano tabelle con prefissi diversi da wp_. Un backup che esporta solo le tabelle con prefisso standard perde ordini, relazioni ACF e dati utenti. Il dump SQL deve sempre coprire l’intero database, non solo le tabelle core.

4. Dimenticare wp-config.php e configurazione server

Un backup completo di WordPress non è solo file e database. Include:

  • wp-config.php con salti, prefisso tabelle e credenziali DB
  • .htaccess o configurazione nginx
  • .user.ini con override PHP
  • Certificati SSL e configurazione DNS
  • Chiavi API di terze parti (Stripe, Mailgun, CDN)

Senza questi, il restore richiede ricostruzione manuale e il RTO passa da 30 minuti a 4 ore.

Automazione end-to-end: cron + REST API + verifica

Ecco un setup completo di automazione che usiamo in produzione. Il cron del server di orchestrazione esegue ogni notte:

# /etc/cron.d/agencypilot-backups
# Backup giornaliero alle 02:00 UTC
0 2 * * * root /usr/local/bin/wp-backup-all-sites.sh

# Verifica restore ogni domenica alle 04:00 UTC
0 4 * * 0 root /usr/local/bin/wp-verify-weekly.sh

# Cleanup retention ogni primo del mese
0 1 1 * * root /usr/local/bin/wp-cleanup-retention.sh

Lo script di verifica settimanale seleziona 3 siti casuali dal portafoglio, esegue un restore test su un container Docker temporaneo e verifica l’integrità. Se un restore fallisce, un alert Slack viene inviato immediatamente al team tecnico.

Backup WordPress e GDPR: dove vivono i tuoi dati

I backup WordPress contengono dati personali: email utenti, indirizzi IP nei log, dati ordini WooCommerce. Sotto GDPR, sei responsabile di dove questi dati risiedono.

Regole pratiche per agenzie europee:

  • Storage in UE: usa region S3 eu-west-1 (Irlanda) o eu-central-1 (Francoforte)
  • Crittografia at-rest: AES-256 sull’object storage, abilitata di default su S3
  • Cifratura in transito: TLS 1.2+ per tutti i trasferimenti via API
  • Accesso loggato: CloudTrail o equivalente per tracciare chi accede ai backup
  • Right to erasure: il backup retention policy deve supportare la cancellazione su richiesta

Per i clienti della Pubblica Amministrazione, i dati devono rimanere in infrastrutture certificate e possibilmente on-premise o in cloud governativo. Vedi il nostro articolo su accessibilità WordPress per la PA per il contesto normativo completo.

Integrazione con il tuo stack di gestione agenzia

Se usi strumenti come ManageWP, MainWP o AgencyPilot per gestire i siti clienti, l’API di backup custom si integra come layer aggiuntivo:

  • AgencyPilot: API nativa per triggerare backup su tutti i siti gestiti
  • ManageWP: backup disponibili come add-on, ma costi che scalano con il numero di siti
  • MainWP: plugin self-hosted, backup tramite estensioni a pagamento

Il vantaggio di un’API custom è l’indipendenza: non sei legato a un pannello specifico e puoi cambiare tool di gestione senza migrare l’infrastruttura di backup.

Monitoring e alerting: sapere quando un backup fallisce

Un backup silenziosamente fallito è peggiore di nessun backup, perché ti dà un falso senso di sicurezza. Il sistema di monitoring deve coprire tre livelli:

Livello Cosa monitorare Alert quando
Processo Exit code script, durata esecuzione Exit code diverso da 0, durata superiore a 30 min
Archivio Dimensione file, hash SHA256 Dimensione sotto soglia min, hash cambiato
Restore Verifica settimanale automatizzata Qualsiasi fallimento del restore test

Usa Prometheus con un exporter custom per raccogliere le metriche e Grafana per visualizzare lo stato dei backup di tutti i siti su un’unica dashboard.

FAQ: Backup WordPress automatico per agenzie

Quanto costa un’infrastruttura di backup WordPress via REST API?

Per 50 siti con backup giornaliero di circa 300MB ciascuno, il costo storage su S3 Standard (14 giorni) + Standard-IA (8 settimane) + Glacier (12 mesi) è di circa 15-20€ al mese. Il costo principale è il tempo di setup iniziale: 4-8 ore per un tecnico senior. Dopo il setup, il costo operativo è vicino a zero.

Posso usare WP-CLI invece della REST API per i backup?

Sì, e per i siti sullo stesso server è spesso preferibile. WP-CLI non ha limiti di memory PHP e può eseguire backup streaming direttamente su S3 senza passare per il web server. La REST API è utile quando i siti sono su server diversi e non hai accesso SSH a tutti. Molti dei nostri articoli su automazione WordPress coprono l’uso di WP-CLI in dettaglio.

Cosa fare se un backup fallisce?

Il protocollo è: 1) Non panicare. 2) Verificare l’ultimo backup valido disponibile. 3) Identificare la causa del failure (memory limit, connessione storage, permessi file). 4) Ri-eseguire il backup dopo aver fissato la causa. 5) Documentare l’incident. Se il failure persiste, passare al backup host-level come fallback temporaneo.

Quanto spesso devo testare il restore?

Almeno una volta alla settimana su un campione casuale di siti. Per siti critici (e-commerce, PA), ogni giorno. Un restore test completo su container Docker temporaneo richiede 5-10 minuti automatizzati. È il singolo investimento con il ROI più alto nella tua infrastruttura di backup.

I backup di hosting (SiteGround, Kinsta, WP Engine) sono sufficienti?

Sono un layer, non una strategia completa. I backup host-level sono veloci da ripristinare ma: 1) hanno retention limitata (14-30 giorni), 2) spariscono se l’account viene sospeso, 3) non sono sotto il tuo controllo. La regola 3-2-1 richiede almeno una copia indipendente dal host. Per approfondire vedi la nostra guida su backup WordPress e disaster recovery per agenzie.

Checklist finale: audit della tua strategia di backup

Usa questa checklist per valutare la tua infrastruttura di backup WordPress attuale:

  • ☐ Hai almeno 3 copie dei dati (3-2-1 rule)
  • ☐ Almeno una copia è off-site, fuori dal server del sito
  • ☐ I backup sono automatizzati, nessun intervento manuale richiesto
  • ☐ Hai una retention policy definita con scadenze automatiche
  • ☐ Hai testato il restore almeno una volta nell’ultimo mese
  • ☐ I backup includono database completo, wp-content, wp-config.php
  • ☐ Le tabelle custom (WooCommerce, ACF) sono incluse nel dump SQL
  • ☐ Hai un alerting attivo che notifica i failure in tempo reale
  • ☐ Lo storage è crittografato e in region GDPR-compliant
  • ☐ Hai documentato RTO e RPO per ogni cliente nel contratto SLA

Se anche solo uno di questi punti è non spuntato, hai un gap nella tua strategia di backup. Nel nostro articolo su sicurezza WordPress per agenzie approfondiamo come i backup si integrano con il piano di sicurezza complessivo.

Un backup WordPress automatico per agenzie non è un plugin che installi e dimentichi. È un’infrastruttura che progetti, automatizzi e verifichi continuamente. La REST API di WordPress ti dà gli strumenti per costruire un sistema su misura che scala con il tuo portafoglio clienti, senza costi di licenza ricorrenti e con controllo totale sui tuoi dati.

Gestisci i siti WordPress dei tuoi clienti?

AgencyPilot ti dà report AI, uptime monitoring, backup e portale clienti in un’unica dashboard. Gratis per 3 siti.

Prova gratis
Leggi anche
Tutti gli articoli
Tutti gli articoli