Regex email : l’expression régulière pour valider une adresse e-mail (JavaScript, PHP, HTML)

Une regex email prête à copier, testée sur des cas réels, avec la fonction JavaScript, l’équivalent PHP et l’attribut HTML : ce qu’elle vérifie, ce qu’elle laisse passer, et comment aller plus loin.

Expression régulière pour valider l’adresse email
Réponse rapide

Pour valider le format d’une adresse e-mail, testez-la avec la regex ci-dessous (test() en JavaScript, preg_match() en PHP). Elle accepte prenom.nom+tag@sous.domaine.fr et refuse les points doublés, les espaces et les domaines sans extension. Elle ne garantit pas que la boîte existe : pour cela, il faut envoyer un message ou interroger le domaine.

Une regex email fait une seule chose : vérifier qu’une chaîne a la forme d’une adresse électronique. C’est peu, et c’est indispensable dans tout formulaire d’inscription, de contact ou de commande. Voici l’expression régulière que j’utilise depuis des années, avec ses versions JavaScript, PHP et HTML, et surtout le détail de ce qu’elle accepte et refuse.

La regex email prête à copier

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

Lue de gauche à droite :

  • ^ et $ : la chaîne entière doit être une adresse, pas seulement en contenir une.
  • [^<>()[\]\\.,;:\s@"]+ : la partie locale (avant le @) est faite de caractères autorisés, sans espace ni ponctuation réservée. Les groupes séparés par un point (prenom.nom) sont acceptés, un point doublé ne l’est pas.
  • |(".+") : l’alternative entre guillemets, prévue par la norme, autorise "jean dupont"@example.com.
  • @, puis le domaine : soit une adresse IP entre crochets ([192.168.1.1]), soit des libellés alphanumériques séparés par des points et terminés par une extension d’au moins deux lettres.
Historique

Cette règle a remplacé en octobre 2022 une version dont les antislashs avaient disparu lors de la migration du site en 2021. Si vous aviez copié l’ancienne, remplacez-la : elle laissait passer des adresses malformées.

Ce que la regex vérifie, et ce qu’elle ne vérifie pas

Le tableau ci-dessous est le résultat réel de test() sous Node.js 22 et de preg_match() sous PHP 8.5 sur seize adresses. Les deux moteurs donnent le même verdict.

Adresse testée Résultat Pourquoi
jean.dupont@example.com acceptée le cas standard
jean+news@sub.example.co.uk acceptée le + et les sous-domaines sont légitimes
JEAN@EXAMPLE.COM acceptée le drapeau i ignore la casse
"jean dupont"@example.com acceptée partie locale entre guillemets, prévue par la RFC 5322
jean@[192.168.1.1] acceptée domaine sous forme d’adresse IP
jéan@exemple.fr acceptée la partie locale accepte les caractères non ASCII
jean@localhost refusée pas d’extension après le point
jean@example refusée même raison
jean..dupont@example.com refusée point doublé dans la partie locale
jean@exa_mple.com refusée le tiret bas n’est pas autorisé dans un nom de domaine
jean@exemple.café refusée extension accentuée : voir les limites
a@b.c refusée extension d’une seule lettre
@example.com refusée partie locale vide
jean@.com refusée domaine vide avant l’extension
jean dupont@example.com refusée espace
jean@example.com (espace final) refusée l’ancre $ refuse tout caractère après l’extension : pensez à trim()

Ce qu’aucune regex ne peut vérifier : que le domaine existe, qu’il accepte du courrier et que la boîte est active. personne@gmail.com a une forme parfaite et ne reçoit probablement rien.

Version JavaScript

La fonction prend l’adresse en paramètre, retire les espaces autour, et renvoie true ou false. Le drapeau i rend la comparaison insensible à la casse.

validateEmail.js
/**
 * Vérifie le format d'une adresse 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, grâce au trim

Dans un formulaire

Le cas d’usage typique : bloquer l’envoi et afficher un message tant que l’adresse est mal formée.

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

Le champ d’erreur est un élément visible à côté du champ, pas une boîte alert() : l’utilisateur garde le contexte et peut corriger immédiatement.

Version PHP

La validation côté client est une aide à la saisie, pas une sécurité. Le serveur doit refaire le contrôle : le JavaScript peut être désactivé, et un formulaire peut être appelé directement. La même regex fonctionne avec preg_match(), aux guillemets près.

isValidEmail.php
/**
 * Vérifie le format d'une adresse 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)

Notez le double échappement des antislashs dans la chaîne PHP entre apostrophes : \\\\ dans le code produit \\ dans le motif, qui signifie « un antislash littéral » pour le moteur de regex.

Regex ou filter_var ?

PHP fournit filter_var($email, FILTER_VALIDATE_EMAIL), qui implémente la norme de plus près. Les deux approches sont valables, elles ne tranchent pas tout à fait de la même façon :

Adresse Regex de l’article filter_var
a@b.c refusée acceptée
"jean dupont"@example.com acceptée refusée
jéan@exemple.fr acceptée refusée sans FILTER_FLAG_EMAIL_UNICODE, acceptée avec
jean+news@sub.example.co.uk acceptée acceptée

Pour un formulaire public, filter_var avec le drapeau Unicode est un bon choix par défaut, la regex reste utile quand vous voulez la même règle exactement en JavaScript et en PHP.

Version HTML

Le navigateur sait déjà valider un e-mail : type="email" refuse l’envoi du formulaire si le format est mauvais, et affiche son propre message. L’attribut pattern permet d’ajouter une règle plus stricte, celle de l’article par exemple.

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

Quatre détails : pattern est implicitement ancré, il ne faut donc pas écrire ^ et $, les guillemets du motif s’écrivent &quot; pour ne pas fermer l’attribut, les navigateurs compilent pattern avec le drapeau v, qui exige d’échapper les parenthèses et les crochets à l’intérieur d’une classe de caractères (\(\)\[\]), faute de quoi le motif est ignoré en silence, et la validation HTML se contourne en une ligne dans la console. Elle améliore l’expérience utilisateur, elle ne remplace pas le contrôle serveur.

Cas limites à connaître

  • Le + : jean+boutique@gmail.com est une adresse valide que beaucoup de gens utilisent pour trier leur courrier. Une regex qui la refuse perd des inscriptions.
  • Les extensions longues : .photography, .paris, .technology existent. C’est pourquoi le motif se termine par [a-zA-Z]{2,} et non par une liste fermée d’extensions.
  • Les accents : la partie locale les accepte, le domaine ne les accepte pas dans cette regex (les domaines internationalisés s’écrivent en réalité en Punycode, xn--…, avant d’être résolus).
  • Les espaces autour : un copier-coller depuis un tableur ajoute souvent un espace final. Appelez trim() avant le test, comme le font les deux fonctions ci-dessus.
  • La casse : le domaine est insensible à la casse, la partie locale l’est en théorie sensible, mais aucun fournisseur grand public ne fait la différence. Comparer en minuscules est le comportement attendu.

Tester la regex avant de la déployer

Un jeu d’essai vaut mieux qu’une relecture. En JavaScript, une boucle sur des adresses connues suffit :

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

Pour explorer un motif, regex101 colore chaque groupe et explique ce qu’il capture. Choisissez la saveur ECMAScript pour JavaScript et PCRE2 pour PHP : les deux acceptent cette regex sans modification.

Aller plus loin : vérifier que l’adresse existe

Quand la forme est bonne, trois contrôles supplémentaires réduisent les fausses adresses, du moins coûteux au plus fiable :

  1. Refuser les domaines jetables (adresses temporaires créées pour contourner une inscription) à partir d’une liste maintenue.
  2. Interroger les enregistrements MX du domaine côté serveur : un domaine sans serveur de courrier ne recevra jamais de message. En PHP, checkdnsrr($domaine, 'MX') répond en une ligne.
  3. Envoyer un e-mail de confirmation avec un lien à cliquer. C’est la seule preuve qu’une personne lit cette boîte, et c’est ce que font tous les services sérieux.

Les deux premiers contrôles sont ceux que j’applique sur les formulaires publics de Foliade, le troisième reste la référence dès qu’un compte est créé.

Les autres expressions régulières de validation

Même approche, autres formats : valider une date, une adresse IP, un code postal ou un mot de passe.

Erreurs fréquentes

Oublier les ancres ^ et $ Sans elles, test() accepte « bonjour jean@example.com merci » parce qu’une adresse valide se trouve quelque part dans la chaîne. La fonction de cet article ancre la regex aux deux extrémités.
Valider seulement côté client Le JavaScript se désactive et se contourne. La même vérification doit exister côté serveur, en PHP ici, avant d’enregistrer ou d’envoyer quoi que ce soit.
Croire qu’une adresse valide existe La regex vérifie une forme, pas une boîte. « nimportequoi@gmail.com » passe. Seul un e-mail de confirmation prouve que l’adresse reçoit du courrier.
Refuser les adresses légitimes Les regex trop strictes rejettent le « + » (jean+newsletter@…) ou les nouvelles extensions longues comme .photography. Testez toujours avec des adresses réelles avant de durcir la règle.

JavaScriptPHP

Damien Flandrin Développeur web depuis 2010, créateur de Gekkode et d’Email Impact. Chaque article est testé sur un projet réel avant publication. Contact
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.