Email regex: e-mailadres valideren in JavaScript, PHP en HTML

Een kant-en-klare email regex, getest op echte gevallen, met de JavaScript-functie, het PHP-equivalent en het HTML-attribuut: wat ze controleert, wat ze doorlaat, en hoe je verder gaat.

Email regex: een e-mailadres valideren in JavaScript en PHP
Kort antwoord

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

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,}))$

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.com toe.
  • @, 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.
Geschiedenis

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.

validateEmail.js
/**
 * 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 trim

In een formulier

Het typische gebruiksgeval: het versturen blokkeren en een bericht tonen zolang het adres verkeerd is opgemaakt.

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();
  }
});

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.

isValidEmail.php
/**
 * 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.

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"
>

Vier details. pattern is impliciet verankerd, je schrijft dus geen ^ en $. De aanhalingstekens van het patroon schrijf je als &quot; 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.com is een geldig adres dat veel mensen gebruiken om hun post te sorteren. Een regex die het weigert, verliest registraties.
  • De lange extensies: .photography, .paris, .technology bestaan. 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:

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}`);
}

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:

  1. Wegwerpdomeinen weigeren (tijdelijke adressen die worden aangemaakt om een registratie te omzeilen) op basis van een onderhouden lijst.
  2. 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.
  3. 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

De ankers ^ en $ vergeten Zonder ze accepteert 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.
Alleen aan de kant van de client valideren JavaScript kan worden uitgeschakeld en omzeild. Dezelfde controle moet bestaan aan de kant van de server, hier in PHP, voordat je iets opslaat of verstuurt.
Denken dat een geldig adres bestaat De regex controleert een vorm, geen postbus. “nimportequoi@gmail.com” komt erdoor. Alleen een bevestigingsmail bewijst dat het adres post ontvangt.
Legitieme adressen weigeren Te strenge regexes wijzen de “+” af (jean+newsletter@…) of de nieuwe lange extensies zoals .photography. Test altijd met echte adressen voordat je de regel strenger maakt.

JavaScriptPHP

Damien Flandrin Webdeveloper sinds 2010, maker van Gekkode en Email Impact. Elk artikel wordt vóór publicatie getest op een echt project. Contact
Nieuwsbrief

Nieuwe tests, tutorials en projecten, per e-mail.

Reproduceerbare tests, geversioneerde code, gedateerde resultaten. Nooit spam.