Un bounce è un server di posta che ti spiega perché non ha accettato il tuo messaggio, con un codice a tre cifre. Classificare bene questi codici fa la differenza tra sopprimere un indirizzo davvero morto e buttarne via uno che sarebbe stato recapitato al tentativo successivo. Questa guida costruisce un classificatore di bounce e mostra come eseguire la stessa classificazione prima dell'invio anziché dopo.
In breve#
La prima cifra del codice SMTP decide l'azione: 5xx è un errore permanente (sopprimi), 4xx è temporaneo (riprova). Il dettaglio che conta è il codice di stato esteso: un 5.7.x è un blocco di policy o di reputazione (il server rifiuta te, non sta giudicando la casella), e non devi trattarlo come un indirizzo morto. Un verificatore applica la stessa logica a un test sulla casella, quindi puoi classificare un indirizzo prima che si verifichi un vero bounce.
Le due dimensioni di un bounce#
Ogni bounce ha due proprietà che conviene tenere separate:
- Permanenza: è definitivo (
5xx, un hard bounce) o temporaneo (4xx, un soft bounce)? Da questo dipende se sopprimere o riprovare. - Oggetto: il server sta giudicando la casella ("no such user") o il tuo mittente ("your IP is blocklisted")? Un rifiuto permanente del tuo mittente sembra un hard bounce, ma non va gestito come tale: sopprimere il destinatario non risolve nulla, perché il problema è la tua reputazione.
La guida ai codici di risposta SMTP copre la grammatica completa; classificare significa trasformarla in un'azione.
Un classificatore di bounce#
Ecco un classificatore che legge il codice SMTP e il codice esteso opzionale e restituisce un'azione. Gestisce anche il blocco di policy travestito da 5xx, il caso che sfugge alla maggior parte dei classificatori ingenui:
/**
* Classify a bounce into an action.
* @param {number} code basic SMTP code, e.g. 550
* @param {string} [enhanced] enhanced status code, e.g. "5.1.1"
*/
export function classifyBounce(code, enhanced) {
const cls = Math.floor(code / 100); // 2, 4, or 5
// Policy / reputation block: the server is refusing YOU, not the mailbox.
// Suppressing the recipient would be treating the wrong problem.
if (enhanced?.startsWith("5.7") || enhanced?.startsWith("4.7")) {
return { type: "policy", action: "review", note: "sender reputation, not the mailbox" };
}
if (cls === 5) {
// Permanent: no such user, disabled, or the domain won't relay.
return { type: "hard", action: "suppress", reason: "rejected_email" };
}
if (cls === 4) {
// Temporary: full mailbox, greylisting, or a rate limit.
const fullMailbox = code === 452 || enhanced === "4.2.2";
return {
type: "soft",
action: "retry",
reason: fullMailbox ? "full_mailbox" : "timeout",
};
}
return { type: "unknown", action: "retry" };
}
classifyBounce(550, "5.1.1"); // { type: "hard", action: "suppress" }
classifyBounce(452); // { type: "soft", action: "retry", reason: "full_mailbox" }
classifyBounce(554, "5.7.1"); // { type: "policy", action: "review" } ← don't suppress
La regola incorporata in questo codice: sopprimi solo quando il rifiuto riguarda chiaramente la casella. Un 5.7.x significa che devi sistemare la tua reputazione di invio; sopprimere il destinatario nasconde il segnale senza risolvere nulla.
Classifica prima del bounce, non dopo#
Il limite della classificazione dei bounce è che è reattiva: scopri che un indirizzo è morto solo inviandogli un'email e danneggiando la tua reputazione nel frattempo. La verifica esegue la stessa classificazione su un test SMTP anziché su un invio reale, quindi ottieni l'esito senza il bounce. Gli esiti e i codici motivo corrispondono direttamente alle stesse azioni:
const ACTION_BY_REASON = {
accepted_email: "keep", // deliverable
rejected_email: "suppress", // hard bounce confirmed by probe
invalid_email: "suppress", // bad syntax
invalid_domain: "suppress", // no mail route
low_quality: "suppress", // disposable
low_deliverability: "segment", // catch-all or full mailbox — send carefully
timeout: "retry", // greylisted / no answer yet
unavailable_smtp: "retry", // couldn't reach the server
unknown: "retry",
};
// From a verification result row:
const action = ACTION_BY_REASON[row.reason] ?? "retry";
Stesso albero decisionale, spostato prima nel tempo. Gli indirizzi che avrebbero generato un hard bounce tornano come undeliverable e vengono soppressi prima dell'invio; quelli unknown ricevono un nuovo tentativo invece di un'ipotesi.
Come inserirlo nel ciclo#
In qualunque direzione lo usi, il risultato finisce in un solo posto, la tua lista di soppressione:
- Reattivo: analizza i webhook dei bounce del tuo ESP, esegui
classifyBounce, sopprimi gli hard bounce e pianifica nuovi tentativi per i soft bounce. - Proattivo: esegui un job di verifica in blocco, recupera i risultati con
include=results, fai passare ognireasonattraversoACTION_BY_REASONe applica le azioni: è il ciclo esporta-verifica-sopprimi che le guide alla pulizia per ESP illustrano piattaforma per piattaforma.
L'ideale è tenerli entrambi: verifica in modo proattivo per prevenire la maggior parte dei bounce e classifica in modo reattivo quelli residui, per intercettare gli indirizzi che nel frattempo sono diventati inattivi.
Domande frequenti
01Qual è la differenza tra hard e soft bounce, a livello di codice?
La classe SMTP: 5xx è un hard bounce (permanente: sopprimi), 4xx è un soft bounce (temporaneo: riprova). L'unica insidia è un codice esteso 5.7.x, cioè un blocco di policy permanente contro il tuo mittente, non una casella morta: classificalo a parte e sistema la reputazione anziché sopprimere il destinatario.
02Quante volte devo riprovare un soft bounce prima di sopprimere l'indirizzo?
Non esiste un numero universale, ma uno schema comune è riprovare un indirizzo in soft bounce per alcuni invii consecutivi e passarlo tra i soppressi se non si riprende mai: più o meno è quello che fanno internamente provider di posta ed ESP. Le caselle piene spesso si svuotano da sole, quindi i soft bounce meritano una pazienza che un hard bounce non merita.
03Posso classificare gli indirizzi prima di inviare loro un'email?
Sì: è esattamente questo la verifica. Un test SMTP sulla casella produce gli stessi segnali di un bounce, associati a un esito deliverable | risky | undeliverable | unknown con un codice motivo, quindi puoi sopprimere gli indirizzi morti prima che generino un bounce. La mappa ACTION_BY_REASON qui sopra trasforma questi esiti nelle stesse azioni di suppress/retry/keep.
Vuoi gli esiti su cui basare la classificazione? Il piano Free include 100 crediti e la documentazione API descrive i codici motivo e l'endpoint dei risultati /jobs da cui legge il ciclo proattivo.



