
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
^(([^<>()[\]\\.,;:\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.
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.
/**
* 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 trimDentro un modulo
Il caso d’uso tipico: bloccare l’invio e mostrare un messaggio finché l’indirizzo è malformato.
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.
/**
* 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.
<label for="email">Adresse e-mail</label>
<input
id="email"
name="email"
type="email"
required
autocomplete="email"
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,}))"
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 " 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,.technologyesistono. 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:
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:
- Rifiutare i domini usa e getta (indirizzi temporanei creati per aggirare un’iscrizione) a partire da un elenco mantenuto aggiornato.
- 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. - 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
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à.jean+newsletter@…) o le nuove estensioni lunghe come .photography. Prova sempre con indirizzi reali prima di irrigidire la regola.

