Skip to content
Commencez avec 100 crédits de vérification gratuits
Qualisend
Tous les articles
Ingénierie / 22 juillet 2026

Comment valider une adresse e-mail en PHP

8 minutes read

Qualisend team
Un schéma en couches montrant une adresse e-mail PHP qui traverse la validation de syntaxe par filter_var, une recherche MX par checkdnsrr et une sonde SMTP de la boîte aux lettres, jusqu'à un verdict unique de délivrabilité

Valider une adresse e-mail en PHP commence et se termine généralement par une seule ligne : filter_var($email, FILTER_VALIDATE_EMAIL). C'est intégré, c'est rapide et c'est réellement utile — mais cela ne répond qu'à la première des trois questions que pose une vraie validation. L'adresse a-t-elle la bonne forme ? Son domaine peut-il recevoir du courrier ? La boîte aux lettres existe-t-elle vraiment ? La bibliothèque standard de PHP répond aux deux premières d'emblée ; la troisième est un problème réseau qu'il vaut la peine de déléguer. Ce guide construit la validation d'e-mail en PHP par couches, avec du code fonctionnel pour chacune, et montre exactement où filter_var s'arrête.

La réponse courte#

Utilisez filter_var() avec FILTER_VALIDATE_EMAIL pour la syntaxe, checkdnsrr() ou getmxrr() pour la recherche MX, et une API de vérification pour le contrôle SMTP de la boîte aux lettres — la moins coûteuse d'abord, en court-circuitant dès que l'une est décisive. N'essayez pas d'ouvrir une session SMTP depuis PHP pour sonder les boîtes aux lettres vous-même : le port 25 sortant est bloqué sur la plupart des hébergements, et la réponse dépend de la réputation de l'IP émettrice et du greylisting que vous ne voulez pas réimplémenter. Chaque couche écarte des adresses à moindre coût que la précédente ; seule la dernière peut valider une adresse.

Couche 1 : la syntaxe avec filter_var#

Pour PHP, filter_var est le bon outil pour la première couche — il vous évite d'écrire une expression régulière à la main, et il est mieux testé que tout ce que vous colleriez depuis Stack Overflow. FILTER_VALIDATE_EMAIL renvoie l'adresse en cas de succès et false en cas d'échec, alors comparez de façon stricte :

function is_valid_syntax(string $email): bool
{
    return strlen($email) <= 320
        && filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
is_valid_syntax('jane@example.com'); // true
is_valid_syntax('not-an-email');     // false
is_valid_syntax('a@@b.com');         // false

Le !== false strict a son importance. filter_var renvoie la chaîne filtrée en cas de succès, pas true, si bien qu'un if (filter_var(...)) souple ne fonctionne que parce qu'une chaîne non vide est « truthy » — comparer à false exprime ce que vous voulez dire et survit aux refactorisations. Le garde-fou sur la longueur relève de la ceinture et des bretelles : une adresse de plus de 320 caractères ne peut pas être réelle, et il est moins coûteux de la rejeter tôt que de transmettre une chaîne géante à quoi que ce soit en aval.

Sachez ce que ce filtre fait et ne fait pas. Par défaut, il valide selon une grammaire dérivée des RFC et rejette les parties locales internationalisées (Unicode) — ainsi 用户@example.com échoue sauf si vous ajoutez le drapeau FILTER_FLAG_EMAIL_UNICODE sur PHP 7.1 et supérieur. C'est un filtre de syntaxe, point final : il ne touche jamais au DNS, n'ouvre jamais de socket, et n'a aucune idée de savoir si example.com existe. Notez aussi que FILTER_SANITIZE_EMAIL est une tout autre bête — il supprime les caractères non autorisés et renvoie une chaîne modifiée plutôt qu'une réponse par oui ou par non, alors ne le sortez pas quand vous voulez valider.

Pourquoi une réussite de filter_var n'est pas un feu vert#

Une réussite de filter_var signifie que la chaîne a la forme d'une adresse e-mail. Elle ne dit rien sur le fait que le courrier arrivera. definitely-fake@gmail.com passe. info@company-that-folded.com passe. typo@gmial.com passe. Toutes les trois sont non délivrables, et aucune correspondance de motif — filter_var ou l'expression régulière la plus élaborée que vous puissiez trouver — ne vous le dira jamais, parce que la syntaxe et la délivrabilité sont des questions différentes. L'une est un fait au sujet de la chaîne ; l'autre est un fait au sujet d'Internet. C'est le même piège qui fait échouer la validation d'e-mail par regex, et filter_var se trouve exactement du même côté de la ligne.

Si vous voulez constater l'écart par vous-même, collez une adresse syntaxiquement parfaite dans le vérificateur d'e-mail gratuit et regardez une chaîne approuvée par filter_var revenir undeliverable.

Couche 2 : le domaine peut-il recevoir du courrier ?#

C'est là que PHP se rend utile sans aucune dépendance. Un domaine sans route de messagerie ne peut accepter de courrier pour personne, si bien que cette seule recherche élimine les domaines morts, les noms d'entreprise mal orthographiés et les TLD inventés. Le contrôle le plus rapide est checkdnsrr(), qui renvoie un booléen indiquant si un type d'enregistrement donné existe :

function has_mail_route(string $domain): bool
{
    // checkdnsrr() returns true if the domain publishes at least one MX record.
    // Fall back to A for domains that accept mail on an implicit MX.
    return checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A');
}
has_mail_route('gmail.com');               // true
has_mail_route('company-that-folded.com'); // false

Séparez le domaine de l'adresse avec strrpos afin de toujours couper au dernier @ :

$domain = substr($email, strrpos($email, '@') + 1);

Lorsque vous avez besoin des hôtes de messagerie réels plutôt que d'un oui/non — par exemple pour les journaliser ou en inspecter les priorités — tournez-vous vers getmxrr(). Il remplit un tableau d'hôtes et un tableau de poids correspondant par référence, et renvoie false lorsqu'il n'y a aucun enregistrement MX du tout :

function mail_hosts(string $domain): array
{
    $hosts = [];
    $weights = [];
    if (!getmxrr($domain, $hosts, $weights)) {
        return [];
    }
    array_multisort($weights, $hosts); // lowest weight = highest priority
    return $hosts; // e.g. ['gmail-smtp-in.l.google.com', 'alt1.gmail-smtp-in.l.google.com', ...]
}

Couche 3 : la boîte aux lettres existe-t-elle vraiment ?#

Les couches un et deux ne peuvent qu'écarter une adresse. Un domaine peut publier des enregistrements MX parfaits et n'avoir malgré tout aucune boîte aux lettres à l'adresse que vous détenez — noreply-9f2x@gmail.com est une syntaxe valide sur un domaine doté d'une route de messagerie active, et c'est pourtant une boîte aux lettres qui n'a jamais été créée. Confirmer qu'une boîte aux lettres précise existe implique la conversation de remise SMTP : se connecter à l'hôte de messagerie, émettre RCPT TO, lire la réponse et se déconnecter avant d'envoyer quoi que ce soit. Il y a plus à dire — comment fonctionne la vérification d'e-mail parcourt tout le pipeline, y compris les domaines catch-all qui acceptent toutes les adresses et déjouent une sonde naïve.

En principe, vous pouvez scripter cela en PHP avec fsockopen() et des commandes SMTP brutes. En pratique, vous ne devriez pas l'exécuter depuis votre serveur applicatif : la plupart des fournisseurs cloud bloquent le port 25 sortant, la réponse dépend de la réputation de l'IP depuis laquelle vous vous connectez, et les serveurs récepteurs appliquent du greylisting et limitent le débit des expéditeurs inconnus — si bien qu'une sonde qui fonctionne dans un test local échoue silencieusement, ou vous fait mettre sur liste de blocage, en production. C'est la couche qu'il vaut la peine de déléguer.

Effectuer le contrôle complet avec une API#

Le POST /verify de Qualisend exécute tout le pipeline — syntaxe, DNS et la sonde SMTP de la boîte aux lettres — depuis une infrastructure à réputation gérée conçue pour cela, et renvoie un verdict. Sans aucun framework, un petit assistant cURL couvre tous les appels que vous ferez :

const QUALISEND_BASE = 'https://app.qualisend.com/api/v1';

function qualisend(string $method, string $path, ?array $body = null): array
{
    $ch = curl_init(QUALISEND_BASE . $path);
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . getenv('QUALISEND_API_KEY'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => $body !== null ? json_encode($body) : null,
        CURLOPT_TIMEOUT    => 10,
    ]);

    $raw    = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($raw === false || $status >= 400) {
        throw new RuntimeException("Qualisend responded {$status}");
    }
    return json_decode($raw, true);
}

function verify(string $email): array
{
    return qualisend('POST', '/verify', ['email' => $email]);
}

Si vous utilisez déjà Guzzle, la même requête tient en quelques lignes de moins et gère le JSON et les codes de statut d'erreur à votre place :

use GuzzleHttp\Client;

$http = new Client(['base_uri' => 'https://app.qualisend.com/api/v1/']);

$response = $http->post('verify', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('QUALISEND_API_KEY')],
    'json'    => ['email' => 'jane@example.com'],
    'timeout' => 10,
]);
$result = json_decode((string) $response->getBody(), true);

Les contrôles locaux reviennent immédiatement, la sonde SMTP étant mise en file d'attente :

{
  "job_id": "6f1c2e0a-9b3d-4a1e-8c77-1a2b3c4d5e6f",
  "probe_queued": true,
  "result": {
    "email": "jane@example.com",
    "status": "deliverable",
    "score": 95,
    "sub_flags": { "role": false, "disposable": false, "free": false, "catch_all": false },
    "did_you_mean": null,
    "smtp": "pending",
    "reason": null
  }
}

Pour la validation d'inscription en direct, le result immédiat suffit généralement pour agir — rejetez undeliverable, proposez la correction did_you_mean, signalez disposable. Lorsque vous avez besoin du verdict confirmé par SMTP, interrogez le job jusqu'à ce que la sonde se termine :

function verify_and_wait(string $email, int $tries = 10, float $delay = 1.5): ?array
{
    $jobId = verify($email)['job_id'];

    for ($i = 0; $i < $tries; $i++) {
        $job = qualisend('GET', "/jobs/{$jobId}?include=results");
        if ($job['status'] === 'completed') {
            return $job['results'][0]; // ['status' => ..., 'score' => ..., 'reason' => ...]
        }
        usleep((int) ($delay * 1_000_000));
    }
    return null; // still processing — treat as unknown, retry later
}

Assembler les couches#

La moins coûteuse d'abord, arrêtez-vous dès que vous avez une réponse :

function validate_email(string $email): array
{
    if (!is_valid_syntax($email)) {
        return ['status' => 'undeliverable', 'reason' => 'invalid_email'];
    }

    $domain = substr($email, strrpos($email, '@') + 1);
    if (!has_mail_route($domain)) {
        return ['status' => 'undeliverable', 'reason' => 'invalid_domain'];
    }

    return verify($email)['result']; // deliverable | risky | undeliverable | unknown
}

Les deux couches locales ne coûtent rien et attrapent la plupart des déchets instantanément ; la couche API ne s'exécute que sur les adresses qui valent l'aller-retour réseau. Cet ordonnancement est toute l'astuce — le même schéma que vous retrouverez dans les versions Node.js et Python de ce guide, parce que c'est le découpage en couches, et non le langage, qui fait fonctionner la validation.

Foire aux questions#

filter_var suffit-il pour valider une adresse e-mail en PHP ?#

Pour la syntaxe, oui — filter_var($email, FILTER_VALIDATE_EMAIL) est le bon contrôle de première couche et un meilleur choix qu'une expression régulière écrite à la main. Mais il valide la forme, pas la délivrabilité : il ne résout jamais le DNS ni ne contacte de serveur de messagerie, si bien qu'une réussite signifie « ressemble à un e-mail », pas « sera délivré ». Associez-le à une recherche MX et à un contrôle SMTP de la boîte aux lettres avant d'accorder votre confiance à l'adresse.

Quelle est la différence entre checkdnsrr() et getmxrr() ?#

checkdnsrr() répond à une question par oui ou par non — le domaine publie-t-il un enregistrement d'un type donné ? — et renvoie un booléen, ce qui suffit pour confirmer qu'une route de messagerie existe. getmxrr() va plus loin : il remplit par référence des tableaux avec les noms d'hôtes MX réels et leurs poids, alors utilisez-le lorsque vous voulez inspecter ou trier les serveurs de messagerie plutôt que simplement confirmer leur présence. Tous deux renvoient false lorsqu'il n'y a aucun enregistrement correspondant.

Puis-je vérifier une boîte aux lettres en PHP sans service externe ?#

En partie. checkdnsrr() et getmxrr() confirment que le domaine accepte le courrier, ce qui écarte gratuitement les domaines morts et ne requiert rien de plus que la bibliothèque standard. Confirmer la boîte aux lettres implique une conversation SMTP, que vous pouvez tenter avec fsockopen() mais ne devriez pas exécuter depuis votre serveur applicatif — le port 25 est largement bloqué et le résultat dépend de la réputation de votre IP. C'est la couche qu'un service de vérification existe pour prendre en charge.

Dois-je effectuer ces contrôles à l'inscription ou lors du nettoyage d'une liste ?#

Les deux, à des profondeurs différentes. Effectuez la syntaxe et la recherche MX de façon synchrone à l'inscription — elles sont assez rapides pour bloquer la requête et donner un retour instantané — et agissez aussi sur le verdict immédiat de l'API à cet endroit. Réservez le résultat complet confirmé par SMTP au nettoyage de listes et aux traitements d'arrière-guichet plus lents ; le guide d'inscription serverless présente le schéma de bout en bout.


Prêt à ajouter la couche SMTP ? Le forfait gratuit inclut 100 crédits qui exécutent le pipeline complet, et la référence API présente les endpoints /verify et /jobs complets avec des exemples prêts à copier-coller dans sept langages.

Your reputation, protected.

Clean your first list in minutes. 100 free credits, no card required.

Get started