
Om het formaat van een e-mailadres te valideren, test je het met de regex hieronder (test() in JavaScript, preg_match() in PHP). Ze accepteert prenom.nom+tag@sous.domaine.fr en weigert twee opeenvolgende punten, spaties en domeinen zonder extensie. Ze garandeert niet dat de postbus bestaat: daarvoor moet je een bericht sturen of het domein bevragen.
Een email regex doet één ding: controleren of een tekenreeks de vorm heeft van een e-mailadres. Dat is weinig, en het is onmisbaar in elk registratie-, contact- of bestelformulier. Hier is de reguliere expressie die ik al jaren gebruik, met de versies voor JavaScript, PHP en HTML, en vooral het detail van wat ze accepteert en weigert.
De kant-en-klare email regex
^(([^<>()[\]\\.,;:\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,}))$Van links naar rechts gelezen:
^en$: de hele tekenreeks moet een adres zijn, niet alleen er een bevatten.[^<>()[\]\\.,;:\s@"]+: het lokale deel (vóór de@) bestaat uit toegestane tekens, zonder spatie of gereserveerde leestekens. Groepen die door een punt worden gescheiden (prenom.nom) zijn toegestaan, twee punten na elkaar niet.|(".+"): het alternatief tussen aanhalingstekens, voorzien in de norm, staat"jean dupont"@example.comtoe.@, gevolgd door het domein: hetzij een IP-adres tussen vierkante haken ([192.168.1.1]), hetzij alfanumerieke labels gescheiden door punten en afgesloten met een extensie van minstens twee letters.
Deze regel heeft in oktober 2022 een versie vervangen waarvan de backslashes verloren waren gegaan bij de migratie van de site in 2021. Had je de oude gekopieerd, vervang haar dan: ze liet misvormde adressen door.
Wat de regex controleert, en wat niet
Onderstaande tabel is het echte resultaat van test() onder Node.js 22 en van preg_match() onder PHP 8.5 op zestien adressen. Beide engines geven hetzelfde oordeel.
| Getest adres | Resultaat | Waarom |
|---|---|---|
jean.dupont@example.com | geaccepteerd | het standaardgeval |
jean+news@sub.example.co.uk | geaccepteerd | de + en de subdomeinen zijn legitiem |
JEAN@EXAMPLE.COM | geaccepteerd | de vlag i negeert hoofdlettergebruik |
"jean dupont"@example.com | geaccepteerd | lokaal deel tussen aanhalingstekens, voorzien door RFC 5322 |
jean@[192.168.1.1] | geaccepteerd | domein in de vorm van een IP-adres |
jéan@exemple.fr | geaccepteerd | het lokale deel accepteert niet-ASCII-tekens |
jean@localhost | geweigerd | geen extensie na de punt |
jean@example | geweigerd | dezelfde reden |
jean..dupont@example.com | geweigerd | twee opeenvolgende punten in het lokale deel |
jean@exa_mple.com | geweigerd | de underscore is niet toegestaan in een domeinnaam |
jean@exemple.café | geweigerd | extensie met accent: zie de beperkingen |
a@b.c | geweigerd | extensie van één letter |
@example.com | geweigerd | lokaal deel leeg |
jean@.com | geweigerd | domein leeg vóór de extensie |
jean dupont@example.com | geweigerd | spatie |
jean@example.com (spatie aan het eind) | geweigerd | het anker $ weigert elk teken na de extensie: denk aan trim() |
Wat geen enkele regex kan controleren: of het domein bestaat, of het post accepteert en of de postbus actief is. personne@gmail.com heeft een perfecte vorm en ontvangt waarschijnlijk niets.
JavaScript-versie
De functie neemt het adres als parameter, verwijdert de spaties eromheen, en geeft true of false terug. De vlag i maakt de vergelijking ongevoelig voor hoofdlettergebruik.
/**
* Controleert het formaat van een e-mailadres.
* @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, dankzij trimIn een formulier
Het typische gebruiksgeval: het versturen blokkeren en een bericht tonen zolang het adres verkeerd is opgemaakt.
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();
}
});Het foutveld is een zichtbaar element naast het invoerveld, geen alert()-venster: de gebruiker behoudt de context en kan meteen corrigeren.
PHP-versie
Validatie aan de kant van de client is een hulp bij het invoeren, geen beveiliging. De server moet de controle overdoen: JavaScript kan worden uitgeschakeld, en een formulier kan rechtstreeks worden aangeroepen. Dezelfde regex werkt met preg_match(), op de aanhalingstekens na.
/**
* Controleert het formaat van een e-mailadres.
*/
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)Let op de dubbele escaping van de backslashes in de PHP-string tussen aanhalingstekens: \\\\ in de code levert \\ op in het patroon, wat “een letterlijke backslash” betekent voor de regex-engine.
Regex of filter_var?
PHP biedt filter_var($email, FILTER_VALIDATE_EMAIL), dat de norm nauwkeuriger implementeert. Beide aanpakken zijn geldig, ze beslissen niet helemaal op dezelfde manier:
| Adres | Regex van dit artikel | filter_var |
|---|---|---|
a@b.c | geweigerd | geaccepteerd |
"jean dupont"@example.com | geaccepteerd | geweigerd |
jéan@exemple.fr | geaccepteerd | geweigerd zonder FILTER_FLAG_EMAIL_UNICODE, geaccepteerd met |
jean+news@sub.example.co.uk | geaccepteerd | geaccepteerd |
Voor een openbaar formulier is filter_var met de Unicode-vlag standaard een goede keuze. De regex blijft nuttig als je precies dezelfde regel wilt in JavaScript en in PHP.
HTML-versie
De browser weet al hoe hij een e-mailadres moet valideren: type="email" weigert het formulier te versturen als het formaat verkeerd is, en toont zijn eigen bericht. Het attribuut pattern maakt het mogelijk een strengere regel toe te voegen, zoals die van dit artikel.
<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"
>Vier details. pattern is impliciet verankerd, je schrijft dus geen ^ en $. De aanhalingstekens van het patroon schrijf je als " zodat het attribuut niet voortijdig sluit. Browsers compileren pattern met de vlag v, die vereist dat je de haakjes en vierkante haken binnen een tekenklasse escaped (\(\)\[\]), anders wordt het patroon stilzwijgend genegeerd. En de HTML-validatie omzeil je met één regel in de console. Ze verbetert de gebruikerservaring, ze vervangt de servercontrole niet.
Grensgevallen om te kennen
- De
+:jean+boutique@gmail.comis een geldig adres dat veel mensen gebruiken om hun post te sorteren. Een regex die het weigert, verliest registraties. - De lange extensies:
.photography,.paris,.technologybestaan. Daarom eindigt het patroon op[a-zA-Z]{2,}en niet op een gesloten lijst van extensies. - Accenten: het lokale deel accepteert ze, het domein accepteert ze niet in deze regex (geïnternationaliseerde domeinen worden in werkelijkheid in Punycode geschreven,
xn--…, voordat ze worden opgelost). - Spaties eromheen: kopiëren en plakken vanuit een spreadsheet voegt vaak een spatie aan het eind toe. Roep
trim()aan vóór de test, zoals de twee functies hierboven doen. - Hoofdlettergebruik: het domein is ongevoelig voor hoofdletters. Het lokale deel is dat in theorie wel, maar geen enkele grote provider maakt het verschil. Vergelijken in kleine letters is het verwachte gedrag.
De regex testen vóór je haar inzet
Een testset is beter dan een herlezing. In JavaScript volstaat een lus over bekende adressen:
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}`);
}Om een patroon te verkennen, kleurt regex101 elke groep en legt uit wat hij vastlegt. Kies de ECMAScript-smaak voor JavaScript en PCRE2 voor PHP: beide accepteren deze regex zonder aanpassing.
Verder gaan: controleren of het adres bestaat
Als de vorm goed is, verminderen drie extra controles het aantal valse adressen, van het minst kostbare naar het meest betrouwbare:
- Wegwerpdomeinen weigeren (tijdelijke adressen die worden aangemaakt om een registratie te omzeilen) op basis van een onderhouden lijst.
- De MX-records van het domein bevragen aan de kant van de server: een domein zonder mailserver zal nooit een bericht ontvangen. In PHP antwoordt
checkdnsrr($domaine, 'MX')in één regel. - Een bevestigingsmail versturen met een link om op te klikken. Dat is het enige bewijs dat iemand die postbus leest, en dat is wat elke serieuze dienst doet.
De eerste twee controles zijn degene die ik toepas op de openbare formulieren van Foliade. De derde blijft de referentie zodra een account wordt aangemaakt.
De andere reguliere expressies voor validatie
Dezelfde aanpak, andere formaten: een datum valideren, een IP-adres, een postcode of een wachtwoord.
Veelgemaakte fouten
test() “bonjour jean@example.com merci” omdat er ergens in de tekenreeks een geldig adres staat. De functie uit dit artikel verankert de regex aan beide uiteinden.jean+newsletter@…) of de nieuwe lange extensies zoals .photography. Test altijd met echte adressen voordat je de regel strenger maakt.

