Newsletter

Regex email: validare un indirizzo e-mail in JavaScript, PHP e HTML

Una regex email pronta da copiare, testata su casi reali, con la funzione JavaScript, l'equivalente PHP e l'attributo HTML: che cosa verifica, che cosa lascia passare e come andare oltre.

Espressione regolare per validare un’email in JavaScript e PHP
Risposta rapida

Per validare il formato di un indirizzo e-mail, testalo con la regex qui sotto (test() in JavaScript, preg_match() in PHP). Accetta prenom.nom+tag@sous.domaine.fr e rifiuta i punti doppi, gli spazi e i domini senza estensione. Non garantisce che la casella esista: per questo bisogna inviare un messaggio o interrogare il dominio.

Una regex email fa una cosa sola: verifica che una stringa abbia la forma di un indirizzo di posta elettronica. È poco, ed è indispensabile in qualsiasi modulo di iscrizione, di contatto o di ordine. Ecco l’espressione regolare che uso da anni, con le sue versioni JavaScript, PHP e HTML, e soprattutto il dettaglio di ciò che accetta e di ciò che rifiuta.

La regex email pronta da copiare

javascript
^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$

Letta da sinistra a destra:

  • ^ e $: l’intera stringa deve essere un indirizzo, non limitarsi a contenerne uno.
  • [^<>()[\]\\.,;:\s@"]+: la parte locale (prima della @) è composta da caratteri ammessi, senza spazi né punteggiatura riservata. I gruppi separati da un punto (prenom.nom) sono accettati, un punto doppio no.
  • |(".+"): l’alternativa tra virgolette, prevista dallo standard, consente "jean dupont"@example.com.
  • @, poi il dominio: o un indirizzo IP tra parentesi quadre ([192.168.1.1]), oppure etichette alfanumeriche separate da punti e terminate da un’estensione di almeno due lettere.
Storico

Nell’ottobre 2022 questa regola ha sostituito una versione i cui backslash erano spariti durante la migrazione del sito nel 2021. Se avevi copiato quella vecchia, sostituiscila: lasciava passare indirizzi malformati.

Che cosa verifica la regex e che cosa non verifica

La tabella qui sotto riporta il risultato reale di test() su Node.js 22 e di preg_match() su PHP 8.5 per sedici indirizzi. I due motori danno lo stesso verdetto.

Indirizzo testato Risultato Perché
jean.dupont@example.com accettato il caso standard
jean+news@sub.example.co.uk accettato il + e i sottodomini sono legittimi
JEAN@EXAMPLE.COM accettato il flag i ignora le maiuscole
"jean dupont"@example.com accettato parte locale tra virgolette, prevista dalla RFC 5322
jean@[192.168.1.1] accettato dominio in forma di indirizzo IP
jéan@exemple.fr accettato la parte locale accetta i caratteri non ASCII
jean@localhost rifiutato nessuna estensione dopo il punto
jean@example rifiutato stesso motivo
jean..dupont@example.com rifiutato punto doppio nella parte locale
jean@exa_mple.com rifiutato il trattino basso non è ammesso in un nome di dominio
jean@exemple.café rifiutato estensione accentata: vedi i limiti
a@b.c rifiutato estensione di una sola lettera
@example.com rifiutato parte locale vuota
jean@.com rifiutato dominio vuoto prima dell’estensione
jean dupont@example.com rifiutato spazio
jean@example.com (spazio finale) rifiutato l’ancora $ rifiuta qualsiasi carattere dopo l’estensione: ricordati il trim()

Quello che nessuna regex può verificare: che il dominio esista, che accetti posta e che la casella sia attiva. personne@gmail.com ha una forma perfetta e con ogni probabilità non riceve nulla.

Versione JavaScript

La funzione riceve l’indirizzo come parametro, elimina gli spazi ai bordi e restituisce true o false. Il flag i rende il confronto insensibile alle maiuscole.

validateEmail.js
/**
 * Verifica il formato di un indirizzo e-mail.
 * @param {string} email
 * @returns {boolean}
 */
function validateEmail(email) {
  const emailReg = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
  return emailReg.test(String(email).trim());
}

console.log(validateEmail('jean.dupont@example.com')); // true
console.log(validateEmail('jean..dupont@example.com')); // false
console.log(validateEmail(' jean@example.com '));      // true, grazie al trim

Dentro un modulo

Il caso d’uso tipico: bloccare l’invio e mostrare un messaggio finché l’indirizzo è malformato.

formulaire.js
const form = document.querySelector('#inscription');
const champ = document.querySelector('#email');
const erreur = document.querySelector('#email-erreur');

form.addEventListener('submit', (event) => {
  if (!validateEmail(champ.value)) {
    event.preventDefault();
    erreur.textContent = 'Adresse e-mail invalide';
    champ.setAttribute('aria-invalid', 'true');
    champ.focus();
  }
});

Il campo di errore è un elemento visibile accanto al campo, non una finestra alert(): l’utente mantiene il contesto e può correggere subito.

Versione PHP

La validazione lato client è un aiuto alla compilazione, non una misura di sicurezza. Il server deve rifare il controllo: JavaScript si può disattivare e un modulo si può richiamare direttamente. La stessa regex funziona con preg_match(), a meno delle virgolette.

isValidEmail.php
/**
 * Verifica il formato di un indirizzo e-mail.
 */
function isValidEmail(string $email): bool
{
    $pattern = '/^(([^<>()[\]\\\\.,;:\s@"]+(\.[^<>()[\]\\\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i';

    return preg_match($pattern, trim($email)) === 1;
}

var_dump(isValidEmail('jean.dupont@example.com')); // bool(true)
var_dump(isValidEmail('jean@localhost'));          // bool(false)

Nota il doppio escape dei backslash nella stringa PHP tra apici singoli: \\\\ nel codice produce \\ nel pattern, che per il motore delle regex significa «un backslash letterale».

Regex o filter_var?

PHP mette a disposizione filter_var($email, FILTER_VALIDATE_EMAIL), che segue lo standard più da vicino. Entrambi gli approcci sono validi, ma non decidono esattamente allo stesso modo:

Indirizzo Regex dell’articolo filter_var
a@b.c rifiutato accettato
"jean dupont"@example.com accettato rifiutato
jéan@exemple.fr accettato rifiutato senza FILTER_FLAG_EMAIL_UNICODE, accettato con il flag
jean+news@sub.example.co.uk accettato accettato

Per un modulo pubblico, filter_var con il flag Unicode è una buona scelta predefinita; la regex resta utile quando vuoi esattamente la stessa regola in JavaScript e in PHP.

Versione HTML

Il browser sa già validare un’e-mail: type="email" rifiuta l’invio del modulo se il formato è sbagliato e mostra un proprio messaggio. L’attributo pattern permette di aggiungere una regola più severa, per esempio quella dell’articolo.

formulaire.html
<label for="email">Adresse e-mail</label>
<input
  id="email"
  name="email"
  type="email"
  required
  autocomplete="email"
  pattern="(([^<>\(\)\[\]\\.,;:\s@&quot;]+(\.[^<>\(\)\[\]\\.,;:\s@&quot;]+)*)|(&quot;.+&quot;))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))"
  title="Une adresse de la forme prenom.nom@domaine.fr"
>

Quattro dettagli: pattern è ancorato in modo implicito, quindi non vanno scritti ^ e $; le virgolette del pattern si scrivono &quot; per non chiudere l’attributo; i browser compilano pattern con il flag v, che impone l’escape di parentesi tonde e quadre dentro una classe di caratteri (\(\)\[\]), altrimenti il pattern viene ignorato in silenzio; e la validazione HTML si aggira con una riga in console. Migliora l’esperienza d’uso, non sostituisce il controllo lato server.

Casi limite da conoscere

  • Il +: jean+boutique@gmail.com è un indirizzo valido che molte persone usano per smistare la posta. Una regex che lo rifiuta perde iscrizioni.
  • Le estensioni lunghe: .photography, .paris, .technology esistono. Per questo il pattern finisce con [a-zA-Z]{2,} e non con un elenco chiuso di estensioni.
  • Gli accenti: la parte locale li accetta; il dominio no, in questa regex (i domini internazionalizzati si scrivono in realtà in Punycode, xn--…, prima di essere risolti).
  • Gli spazi ai bordi: un copia-incolla da un foglio di calcolo aggiunge spesso uno spazio finale. Chiama trim() prima del test, come fanno le due funzioni qui sopra.
  • Le maiuscole: il dominio è insensibile alle maiuscole; la parte locale in teoria è sensibile, ma nessun provider di massa fa la distinzione. Confrontare in minuscolo è il comportamento atteso.

Testare la regex prima di metterla in produzione

Un set di prova vale più di una rilettura. In JavaScript basta un ciclo su indirizzi noti:

test.js
const cas = {
  'jean.dupont@example.com': true,
  'jean+news@sub.example.co.uk': true,
  'jean@localhost': false,
  'jean..dupont@example.com': false,
  'jean dupont@example.com': false,
};

for (const [email, attendu] of Object.entries(cas)) {
  const ok = validateEmail(email) === attendu;
  console.log(`${ok ? 'OK ' : 'KO '} ${email}`);
}

Per esplorare un pattern, regex101 colora ogni gruppo e spiega che cosa cattura. Scegli il flavour ECMAScript per JavaScript e PCRE2 per PHP: entrambi accettano questa regex senza modifiche.

Andare oltre: verificare che l’indirizzo esista

Quando la forma è corretta, tre controlli aggiuntivi riducono gli indirizzi falsi, dal meno costoso al più affidabile:

  1. Rifiutare i domini usa e getta (indirizzi temporanei creati per aggirare un’iscrizione) a partire da un elenco mantenuto aggiornato.
  2. Interrogare i record MX del dominio lato server: un dominio senza server di posta non riceverà mai un messaggio. In PHP, checkdnsrr($domaine, 'MX') risponde in una riga.
  3. Inviare un’e-mail di conferma con un link da cliccare. È l’unica prova che una persona legge quella casella, ed è quello che fanno tutti i servizi seri.

I primi due controlli sono quelli che applico sui moduli pubblici di Foliade; il terzo resta il riferimento non appena viene creato un account.

Le altre espressioni regolari di validazione

Stesso approccio, altri formati: validare una data, un indirizzo IP, un codice postale o una password.

Errori frequenti

Dimenticare le ancore ^ e $ Senza di esse, test() accetta «bonjour jean@example.com merci» perché un indirizzo valido si trova da qualche parte nella stringa. La funzione di questo articolo ancora la regex a entrambe le estremità.
Validare solo lato client JavaScript si disattiva e si aggira. Lo stesso controllo deve esistere lato server, qui in PHP, prima di salvare o inviare qualsiasi cosa.
Credere che un indirizzo valido esista La regex verifica una forma, non una casella. «nimportequoi@gmail.com» passa. Solo un'e-mail di conferma dimostra che l'indirizzo riceve posta.
Rifiutare indirizzi legittimi Le regex troppo severe scartano il «+» (jean+newsletter@…) o le nuove estensioni lunghe come .photography. Prova sempre con indirizzi reali prima di irrigidire la regola.

JavaScriptPHP

Damien Flandrin Sviluppatore web dal 2010, creatore di Gekkode e di Email Impact. Ogni articolo è testato su un progetto reale prima della pubblicazione. Contatti
Newsletter

I nuovi test, tutorial e progetti, via e-mail.

Test riproducibili, codice versionato, risultati datati. Mai spam.