1 03/05/2026 10 min

APPEND: errore generico, causa quasi mai “nella frase”

Il messaggio “Errore del server: APPEND: Internal error occurred. Refer to server log for more information” non è una diagnosi, è un sintomo. In pratica il sistema ha tentato di aggiungere dati a una risorsa esistente e l’operazione è fallita in modo non specifico. In ambito webmaster questa etichetta compare spesso quando il problema vero sta sotto il livello applicativo: filesystem pieno, permessi errati, backend mail o storage non raggiungibile, modulo PHP che solleva eccezioni, database in sofferenza, oppure un componente intermedio che interrompe la richiesta e restituisce un errore generico.

La prima scelta corretta non è “riavviare tutto”, ma capire quale layer ha fallito: edge, web server, runtime PHP, applicazione, storage o servizio esterno. Se tocchi config o servizi prima di avere un segnale minimo, rischi di spostare il problema e perdere il punto di origine nei log.

Appendi, ma dove? Il layer da isolare per primo

In questo caso la classificazione operativa è troubleshooting con impatto potenziale di produzione. Lo stato atteso è semplice: la richiesta che esegue l’operazione di append dovrebbe completarsi e lasciare traccia nei log applicativi senza errori. Lo stato osservato è una risposta generica del server, quindi il punto utile non è il messaggio lato browser ma la catena che lo genera.

Ordine di probabilità tipico:

  1. Filesystem o storage: disco pieno, inode esauriti, quota raggiunta, mount degradato, permessi non coerenti.
  2. Runtime applicativo: PHP-FPM/CGI in errore, timeout, memory limit, eccezione non gestita, estensione mancante.
  3. Backend dipendente: database, mail store, object storage, coda o API esterna non raggiungibile.

Queste tre ipotesi si falsificano in pochi minuti con controlli mirati: spazio disco, log del servizio, e richiesta manuale ripetuta fuori dal frontend.

Verifiche immediate senza cambiare nulla

L'errore si verifica quando Dovecot non riesce a scrivere nel file indice della cartella Inviati:

  /var/www/UTENTE/data/email/dominio.it/mail/.maildir/.Sent/dovecot.index.log

  Questo file tiene traccia dei messaggi nella cartella. Se i permessi sono sbagliati — ad esempio il proprietario è
  root invece dell'utente della mailbox — Dovecot non può modificarlo e Roundcube restituisce l'errore APPEND.

  ---
  Come succede

  Se sul server è attivo un sistema di rilevamento webshell (come TRGTGUARD), può capitare che il file dovecot.index.log
   venga erroneamente identificato come sospetto. Il motivo è che i file indice di Dovecot sono binari e contengono
  righe molto lunghe che i pattern di rilevamento interpretano come offuscazione.

  Quando il file viene messo in quarantena e Dovecot lo ricrea, lo ricrea con owner root invece dell'utente corretto —
  rompendo i permessi.

  ---
  La soluzione

  1. Correggi i permessi del file:

  chown UTENTE:UTENTE /var/www/UTENTE/data/email/dominio.it/mail/.maildir/.Sent/dovecot.index.log
  chmod 600 /var/www/UTENTE/data/email/dominio.it/mail/.maildir/.Sent/dovecot.index.log

  2. Per tutti i siti in un colpo:

  find /var/www/*/data/email -name "dovecot.index*" | while read f; do
      owner=$(stat -c '%U' "$(dirname "$f")" 2>/dev/null)
      [ -n "$owner" ] && chown "$owner:$owner" "$f" && chmod 600 "$f"
  done

  3. Se usi TRGTGUARD, aggiungi /data/email alla whitelist:

  echo '/data/email' >> /etc/shell-detector/whitelist.txt

  Questo impedisce al shell detector di toccare i file email in futuro.

  ---
  Verifica

  Dopo aver corretto i permessi, riprova a inviare una mail da Roundcube — l'errore APPEND scomparirà immediatamente
  senza bisogno di riavviare alcun servizio.

Parti dall’osservabilità. Se il problema è in produzione, ogni verifica deve essere non distruttiva e reversibile. L’obiettivo è trovare un artefatto concreto: uno stato, un errore, una metrica, una riga di log.

  1. Verifica se il server risponde davvero e con quale codice.
curl -sS -o /dev/null -D - https://tuodominio.tld/percorso/della/funzionalita

Atteso: 2xx o almeno un errore coerente con il layer coinvolto. Se vedi 5xx, il problema è quasi certamente lato origin o applicazione. Se vedi timeout, sospetta backend bloccato, saturazione o rete.

  1. Controlla spazio e inode sul filesystem che ospita webroot, cache, upload o coda.
df -h

 df -i

 mount | grep -E ' /(var|home|srv|data|tmp) '

KO tipico: filesystem al 100%, inode al 100%, mount in sola lettura, o partizione diversa da quella attesa. Se il path dell’app è su un volume separato, verifica quello preciso: spesso il disco “libero” del sistema non coincide con il volume che contiene i dati.

  1. Guarda i log del web server e del runtime nell’intervallo del fallimento.
journalctl -u nginx -u php-fpm --since '10 min ago' --no-pager

# oppure, se usi Apache
journalctl -u apache2 --since '10 min ago' --no-pager

Atteso: una riga con stack trace, permission denied, timeout upstream, memory exhausted, open_basedir, oppure un errore del backend chiamato dall’app. Se i log sono muti, il punto da controllare è la configurazione del logging stesso o un livello di verbosità troppo basso.

  1. Se l’app usa PHP-FPM, controlla saturazione e errori del pool.
systemctl status php-fpm

journalctl -u php-fpm --since '10 min ago' --no-pager

KO tipico: child process terminati, pool in standby sotto carico, errore di configurazione in un file `.conf`, oppure richieste accodate oltre il timeout del proxy.

  1. Se c’è un database dietro l’operazione, verifica connessione e latenza minima.
mysqladmin ping -h 127.0.0.1 -u USER -p

# oppure test applicativo equivalente
php -r 'try { new PDO("mysql:host=127.0.0.1;dbname=DB", "USER", "PASS"); echo "OK\n"; } catch (Throwable $e) { echo $e->getMessage()."\n"; }'

Se il database risponde ma l’app fallisce, il gap è nel livello applicativo: credenziali, schema, permessi sul DB, timeout troppo stretti o query che blocca. Se il database non risponde, non perdere tempo sul frontend.

Soluzione consigliata passo-passo

Qui conta la reversibilità. Ogni passo sotto va letto come: cambio minimo, verifica immediata, poi eventuale scala. Se non hai ancora un artefatto utile dai log, fermati e non fare tuning alla cieca.

  1. Se il disco è pieno o quasi pieno, libera spazio in modo mirato.
    Blast radius: medio, perché tocca storage e potenzialmente log o cache. Rollback: ripristino file archiviati solo se hai verificato che non siano in uso.

Interventi conservativi: ruota log, svuota cache applicative non critiche, rimuovi file temporanei noti, sposta backup vecchi su storage secondario. Evita di cancellare directory “a sensazione”. Se il problema è un mount pieno, il fix corretto è spesso fuori dall’app: quota, retention, o ampliamento del volume.

du -sh /var/log/* 2>/dev/null | sort -h

# esempio di rotazione log, se già prevista dalla tua policy
logrotate -d /etc/logrotate.conf
  1. Se i permessi sono incoerenti, riallinea owner e mode sul solo path coinvolto.
    Blast radius: basso se limiti il comando al path preciso. Rollback: ripristino da backup di ownership/mode se hai una baseline documentata.

Errore classico: file creati da root durante manutenzione, poi l’utente del web server non riesce più ad appendere contenuti o scrivere cache. Verifica il proprietario effettivo prima di cambiare tutto il tree.

stat /percorso/dell/app/files

# esempio: correggere solo la directory necessaria
chown -R www-data:www-data /var/www/app/storage
  1. Se PHP o l’app stanno esaurendo memoria o timeout, aumenta solo il limite che hai misurato come stretto.
    Blast radius: medio, perché puoi aumentare consumo risorse. Rollback: ripristina il valore precedente dopo il test.

Non alzare tutto “per sicurezza”. Se il problema è una query lenta o un loop, più memoria maschera il sintomo ma non risolve. Usa il log per capire se il limite è davvero quello.

; esempio php.ini o pool PHP-FPM
memory_limit = 256M
max_execution_time = 60
  1. Se il backend è lento o giù, applica una mitigazione temporanea.
    Blast radius: variabile, dipende dal servizio. Rollback: disattiva la mitigazione appena il backend torna stabile.

Mitigazioni tipiche: disabilitare temporaneamente una funzionalità non critica, mettere in coda le richieste di append, usare una pagina di manutenzione per evitare errori ripetuti, o deviare il traffico verso cache se l’operazione lo consente. Se hai un CDN o un reverse proxy, valuta un bypass solo per test, non come soluzione permanente.

  1. Se il problema è nel codice, isola la funzione che esegue l’append e aggiungi logging utile.
    Blast radius: basso se modifichi solo logging o gestione eccezioni. Rollback: revert del commit o disattivazione del flag.

Qui la correzione strutturale è spesso semplice: controllare eccezioni, validare il percorso di scrittura, gestire risultati nulli, evitare append concorrenti non protetti. Se l’errore appare solo sotto carico, il problema può essere una race condition o un lock contention sul file o sulla tabella.

Quando “APPEND” arriva da mail, storage o CMS

In alcuni ambienti il messaggio non nasce dal web server ma da un componente specifico. Un mail server può parlare di append quando salva messaggi in mailbox o indice; un CMS può mostrarlo quando aggiunge record a una tabella o a un file di cache; un’app custom può usarlo per qualunque operazione di scrittura incrementale. In questi casi il testo è identico, ma il punto reale cambia.

Per distinguere i casi, cerca tre elementi: path del log, nome del processo e azione immediatamente precedente all’errore. Se il log cita un file `.log` o `.mailbox`, il problema è storage o permessi. Se cita una query o un driver, guarda il database. Se cita un endpoint HTTP, il guasto può essere upstream o applicativo.

Esempi pratici di lettura del log

Un buon log non dice solo “errore”, ma dove e su cosa. Ecco alcuni pattern tipici da cercare.

Permission denied: /var/www/app/storage/cache/tmp123

Interpretazione: permessi o contesto SELinux/AppArmor, non bug logico. Verifica owner, mode e policy di sicurezza.

SQLSTATE[HY000]: General error: 2006 MySQL server has gone away

Interpretazione: database caduto, timeout, connessione lunga o pacchetto troppo grande. Qui l’append fallisce perché il backend non è disponibile.

PHP Fatal error:  Allowed memory size exhausted

Interpretazione: il problema non è l’append in sé, ma il percorso di codice che costruisce il contenuto prima di scriverlo.

upstream timed out (110: Connection timed out) while reading response header from upstream

Interpretazione: il web server ha parlato con il backend ma ha aspettato troppo. Non aumentare il timeout senza capire perché il backend è lento.

Controlli finali e rollback

Dopo la correzione minima, ripeti il test dall’esterno e dal server. L’idea è confermare che il fix non abbia solo “spostato” l’errore.

  1. Riesegui la richiesta che generava l’errore e verifica che il codice HTTP sia coerente con l’operazione attesa.
  2. Controlla che nei log non compaiano nuovi errori nella finestra temporale successiva alla modifica.
  3. Se hai toccato configurazioni, ricarica il servizio invece di riavviare tutto quando basta un reload.
systemctl reload nginx
systemctl reload php-fpm

Rollback: ripristina il file di configurazione precedente, annulla il cambio di ownership o riporta il limite precedente se il problema persiste. Se hai modificato codice, fai revert del commit o disattiva il ramo tramite feature flag. Se il fix ha coinvolto spazio disco o volume, conserva la prova del prima/dopo con `df -h` e i log di rotazione.

Assunzione operativa: il messaggio “APPEND” è stato trattato come errore applicativo o di storage su stack Linux recente con web server e runtime PHP; se il tuo contesto è un prodotto specifico, la chiusura del gap passa dai log del servizio che emette esattamente quella stringa.