Skip to content
Inizia con 100 crediti di verifica gratuiti
Qualisend
Tutti gli articoli
Ingegneria / 14 giugno 2026

Come validare un indirizzo email in Java

7 min di lettura

Qualisend team
Come validare un indirizzo email in Java

Validare un indirizzo email in Java significa in realtà fare tre controlli che vanno sotto un unico nome. La maggior parte dei tutorial ricorre a una regex, conferma che la stringa ha la forma di un indirizzo e considera chiusa la questione, ma la forma è la meno utile delle tre cose che vuoi davvero sapere. La vera validazione è a livelli: un controllo di formato economico, una ricerca DNS dell'instradamento della posta del dominio e un test SMTP della casella. La libreria standard di Java e Jakarta Mail coprono in modo pulito i primi due; il terzo è un problema di rete che conviene delegare. Questa guida costruisce ogni livello con codice funzionante e mostra esattamente dove si ferma ciascuno.

In breve#

Usa jakarta.mail.internet.InternetAddress (o Apache Commons Validator) per la sintassi, il provider DNS per JNDI integrato nel JDK per la ricerca MX e un'API di verifica per il controllo SMTP della casella: prima il più economico, fermandoti appena uno dei controlli è decisivo. Non aprire connessioni SMTP dalla tua app per testare direttamente le caselle: la porta 25 in uscita è bloccata dalla maggior parte degli host e la risposta dipende dalla reputazione dell'IP di invio e dal greylisting, cose che non vuoi reimplementare. Ogni livello esclude indirizzi a un costo minore dell'ultimo, e solo l'ultimo può confermarne uno.

Livello 1: la sintassi con Jakarta Mail#

Jakarta Mail (la libreria nata come javax.mail e rinominata quando è passata al namespace Jakarta EE) include un parser di indirizzi su cui puoi contare invece di scrivere a mano una regex. Crea un InternetAddress e chiama validate(): il costruttore analizza l'indirizzo e validate() applica la sintassi della RFC 822, lanciando un'AddressException per qualsiasi input malformato.

import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;

public static boolean isValidSyntax(String email) {
    if (email == null || email.length() > 320) {
        return false;
    }
    try {
        InternetAddress address = new InternetAddress(email);
        address.validate();
        return true;
    } catch (AddressException e) {
        return false;
    }
}

Una cosa da sapere: validate() è permissivo sul dominio. Accetta jane@localhost e jane@example, senza bisogno del punto, perché la RFC 822 ammette domini senza punto. In questo caso va bene. Il livello 1 è un filtro sul formato, non un controllo di deliverability, ed è la ricerca DNS del livello 2 a decidere davvero se il dominio può ricevere posta. Mantieni permissivo questo controllo e lascia al DNS il compito di escludere; inseguire la perfezione del formato con una regex più grande è comunque una battaglia persa.

Se usi un vecchio jar di javax.mail, il codice è identico a parte il nome del package. E se dipendi già da Apache Commons Validator, il suo EmailValidator è una buona alternativa per il primo livello: è un po' più rigido (rifiuta i domini senza punto per impostazione predefinita):

import org.apache.commons.validator.routines.EmailValidator;

boolean valid = EmailValidator.getInstance().isValid(email);

Preferisci Jakarta Mail se la usi già per inviare posta: una dipendenza in meno. In ogni caso, entrambe le librerie rispondono solo alla domanda sulla sintassi.

Livello 2: il dominio può ricevere posta?#

Un dominio senza record MX non può accettare posta per nessuno, quindi una sola ricerca MX elimina domini inattivi, nomi aziendali scritti male e TLD inventati. Java lo fa senza alcuna dipendenza esterna: il JDK include un service provider DNS per JNDI, quindi risolvi l'attributo MX tramite un DirContext.

import java.util.Hashtable;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public static boolean hasMailRoute(String domain) {
    Hashtable<String, String> env = new Hashtable<>();
    env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
    env.put("com.sun.jndi.dns.timeout.initial", "2000"); // don't hang on a slow resolver
    env.put("com.sun.jndi.dns.timeout.retries", "1");

    DirContext ctx = null;
    try {
        ctx = new InitialDirContext(env);
        Attributes attrs = ctx.getAttributes(domain, new String[] { "MX" });
        Attribute mx = attrs.get("MX");
        return mx != null && mx.size() > 0;
    } catch (NamingException e) {
        // NameNotFoundException → domain doesn't exist; no MX attribute → no mail route
        return false;
    } finally {
        if (ctx != null) {
            try { ctx.close(); } catch (NamingException ignored) { }
        }
    }
}

Separa il dominio dall'indirizzo in corrispondenza dell'ultima @, così le parti locali tra virgolette non ti creano problemi:

String domain = email.substring(email.lastIndexOf('@') + 1);

hasMailRoute("gmail.com");               // true
hasMailRoute("company-that-folded.com"); // false

Un attributo MX mancante o vuoto significa che il dominio non pubblica alcun instradamento della posta; una NameNotFoundException (una sottoclasse di NamingException) significa che il dominio non si risolve affatto. Alcuni domini accettano posta su un record A con un MX implicito: se vuoi gestire anche questo caso limite, ripiega su una query "A" quando l'insieme MX torna vuoto. Per la stragrande maggioranza degli indirizzi reali, un controllo MX è il filtro giusto. Dato che la ricerca resta in attesa di un resolver DNS, mantieni i timeout visti sopra in un flusso di registrazione e tratta un errore temporaneo come "da ricontrollare più tardi", non come un rifiuto netto: un intoppo del DNS non dovrebbe allontanare un cliente reale.

Livello 3: la casella esiste davvero?#

I livelli 1 e 2 possono solo escludere un indirizzo. Un dominio può pubblicare record MX perfetti e non avere comunque alcuna casella all'indirizzo che hai in mano: noreply-9f2x@gmail.com ha una sintassi valida su un dominio con un instradamento di posta attivo, ma resta una casella mai creata. Confermare che una casella specifica esista richiede la conversazione SMTP di consegna: collegarsi all'host di posta, inviare RCPT TO, leggere la risposta e disconnettersi prima di inviare qualsiasi cosa. C'è dell'altro (i domini catch-all accettano qualsiasi indirizzo e rendono inutile un test ingenuo): come funziona la verifica email illustra l'intera pipeline.

Puoi implementare il dialogo SMTP in Java con un Socket grezzo, ma non dovresti farlo dalla tua applicazione. La maggior parte dei provider cloud blocca la porta 25 in uscita, la risposta dipende dalla reputazione dell'IP da cui ti colleghi e i server riceventi applicano il greylisting e limitano la frequenza dei mittenti che non conoscono: così un test che funziona in locale, in produzione smette di funzionare senza avvisi o ti fa finire in blacklist. È questo il livello che conviene delegare.

POST /verify di Qualisend esegue l'intera pipeline (sintassi, DNS e test SMTP della casella) da un'infrastruttura con reputazione gestita, costruita apposta, e restituisce un esito. Java 11+ include java.net.http.HttpClient, quindi non ti serve alcuna dipendenza HTTP; abbinalo a Jackson (o Gson) per leggere il JSON. Tieni la chiave in una variabile d'ambiente e non scriverla mai nel codice:

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class QualisendClient {
    // Placeholder endpoint — check /developers for the current base URL and shape.
    private static final String ENDPOINT = "https://api.qualisend.com/v1/verify";

    private final HttpClient http = HttpClient.newHttpClient();
    private final ObjectMapper mapper = new ObjectMapper();

    public JsonNode verify(String email) throws IOException, InterruptedException {
        String apiKey = System.getenv("QUALISEND_API_KEY"); // holds YOUR_API_KEY
        String payload = mapper.writeValueAsString(Map.of("email", email));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(ENDPOINT))
            .header("Authorization", "Bearer " + apiKey)
            .header("Content-Type", "application/json")
            .timeout(Duration.ofSeconds(10))
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response =
            http.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new IOException("Qualisend responded " + response.statusCode());
        }
        // The verdict lives inside a `result` envelope.
        return mapper.readTree(response.body()).get("result");
    }
}

Leggi i campi che ti interessano dal nodo result:

JsonNode result = client.verify("jane@example.com");

String  status     = result.get("status").asText();  // deliverable | risky | undeliverable | unknown
int     score      = result.path("score").asInt();    // 0–100 confidence
String  reason     = result.hasNonNull("reason")
                         ? result.get("reason").asText()  // machine-readable reason, may be null
                         : null;
boolean disposable = result.path("sub_flags").path("disposable").asBoolean();

La forma esatta della risposta, con ogni chiave di sub_flags e ogni codice di reason, è nel riferimento API. Per la validazione in tempo reale in fase di registrazione, di solito lo status basta per decidere: rifiuta undeliverable, gestisci risky e unknown secondo le tue regole e segnala gli indirizzi disposable già nel campo di input.

Mettere insieme i livelli per validare un indirizzo email in Java#

Prima il più economico, e fermati appena hai una risposta. Un piccolo record contiene l'esito (i record richiedono Java 16+; dalla 11 alla 15 usa una classe normale):

public record Verdict(String status, String reason) {}

public Verdict validate(String email) throws IOException, InterruptedException {
    if (!isValidSyntax(email)) {
        return new Verdict("undeliverable", "invalid_email");
    }
    String domain = email.substring(email.lastIndexOf('@') + 1);
    if (!hasMailRoute(domain)) {
        return new Verdict("undeliverable", "invalid_domain");
    }
    JsonNode result = client.verify(email); // client is a QualisendClient
    return new Verdict(
        result.get("status").asText(),
        result.hasNonNull("reason") ? result.get("reason").asText() : null);
}

I due livelli locali non costano nulla e intercettano subito la maggior parte degli indirizzi spazzatura; l'API viene chiamata solo per gli indirizzi che meritano la chiamata di rete. Tutto il trucco sta in quell'ordine: è la stessa struttura che trovi nelle versioni di questa guida per Node.js, Python e PHP, perché a far funzionare la validazione è la suddivisione in livelli, non il linguaggio.

Domande frequenti#

InternetAddress.validate() basta per validare un'email in Java?#

Per la sintassi è lo strumento giusto del primo livello: jakarta.mail.internet.InternetAddress con validate() è testato meglio di una regex scritta a mano e ti evita di reimplementare la RFC 822. Ma valida solo la forma: non risolve mai il DNS né contatta un server di posta, ed è abbastanza permissivo da accettare domini senza punto come jane@localhost. Abbinalo a una ricerca MX e a un controllo SMTP della casella prima di fidarti dell'indirizzo.

Come controllo i record MX in Java senza una libreria esterna?#

Usa il provider DNS per JNDI integrato nel JDK. Crea un InitialDirContext con java.naming.factory.initial impostato su com.sun.jndi.dns.DnsContextFactory, poi chiama getAttributes(domain, new String[] {"MX"}) e controlla l'attributo MX restituito. Non richiede dipendenze di terze parti e ti dice chiaramente se un dominio pubblica un instradamento della posta. Intercetta NamingException e considera non recapitabile un indirizzo con MX mancante.

Posso verificare una casella in Java senza un'API?#

In parte. Jakarta Mail e JNDI confermano che l'indirizzo è ben formato e che il dominio accetta posta, entrambi gratis e con poche dipendenze. Confermare la casella vera e propria richiede una conversazione SMTP, che puoi tentare con un Socket grezzo ma che non dovresti eseguire dal tuo server applicativo: la porta 25 è bloccata quasi ovunque e il risultato dipende dalla reputazione del tuo IP di invio. È di quest'ultimo livello che si occupa un servizio di verifica.

Meglio validare le email in fase di registrazione o durante la pulizia di una lista?#

Entrambe le cose, con una profondità diversa. Esegui i livelli di sintassi e MX in modo sincrono in fase di registrazione (sono abbastanza veloci da bloccare la richiesta e dare un riscontro immediato) e lì agisci anche in base allo status restituito dall'API. Riserva il lavoro in blocco più approfondito alla pulizia delle liste; la guida alla verifica serverless in fase di registrazione mostra lo schema in tempo reale, e la guida alla pulizia delle liste copre la parte in blocco.


Vuoi aggiungere il livello SMTP? Prova qualsiasi indirizzo con la verifica email gratuita per vedere una stringa sintatticamente perfetta tornare con un esito reale, oppure leggi il riferimento API per l'endpoint /verify, l'oggetto result completo ed esempi pronti da copiare e incollare in tutti i linguaggi citati sopra.

La tua reputazione, protetta..

Pulisci la tua prima lista in pochi minuti. 100 crediti gratuiti, senza carta di credito.

Inizia