Regex email: validar um endereço de email em JavaScript, PHP e HTML

Uma regex email pronta a copiar, testada em casos reais, com a função JavaScript, o equivalente em PHP e o atributo HTML: o que verifica, o que deixa passar e como ir mais longe.

Regex de email: validar um endereço em JavaScript e PHP
Resposta rápida

Para validar o formato de um endereço de email, testa-o com a regex abaixo (test() em JavaScript, preg_match() em PHP). Aceita prenom.nom+tag@sous.domaine.fr e recusa os pontos duplicados, os espaços e os domínios sem extensão. Não garante que a caixa de correio existe: para isso, é preciso enviar uma mensagem ou consultar o domínio.

Uma regex email faz uma única coisa: verificar que uma string tem a forma de um endereço de correio eletrónico. É pouco, e é indispensável em qualquer formulário de registo, de contacto ou de encomenda. Aqui fica a expressão regular que uso há anos, com as versões JavaScript, PHP e HTML e, sobretudo, o detalhe do que aceita e do que recusa.

A regex email pronta a copiar

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

Lida da esquerda para a direita:

  • ^ e $: a string inteira tem de ser um endereço, não basta conter um.
  • [^<>()[\]\\.,;:\s@"]+: a parte local (antes do @) é feita de caracteres autorizados, sem espaços nem pontuação reservada. Os grupos separados por um ponto (prenom.nom) são aceites; um ponto duplicado não é.
  • |(".+"): a alternativa entre aspas, prevista na norma, autoriza "jean dupont"@example.com.
  • @, depois o domínio: ou um endereço IP entre parênteses retos ([192.168.1.1]), ou etiquetas alfanuméricas separadas por pontos e terminadas por uma extensão de pelo menos duas letras.
Histórico

Esta regra substituiu, em outubro de 2022, uma versão cujas barras invertidas tinham desaparecido durante a migração do site em 2021. Se copiaste a antiga, substitui-a: deixava passar endereços malformados.

O que a regex verifica, e o que não verifica

A tabela abaixo é o resultado real de test() em Node.js 22 e de preg_match() em PHP 8.5 sobre dezasseis endereços. Os dois motores dão o mesmo veredicto.

Endereço testado Resultado Porquê
jean.dupont@example.com aceite o caso padrão
jean+news@sub.example.co.uk aceite o + e os subdomínios são legítimos
JEAN@EXAMPLE.COM aceite a flag i ignora maiúsculas e minúsculas
"jean dupont"@example.com aceite parte local entre aspas, prevista na RFC 5322
jean@[192.168.1.1] aceite domínio sob a forma de endereço IP
jéan@exemple.fr aceite a parte local aceita caracteres não ASCII
jean@localhost recusado não há extensão depois do ponto
jean@example recusado mesma razão
jean..dupont@example.com recusado ponto duplicado na parte local
jean@exa_mple.com recusado o underscore não é permitido num nome de domínio
jean@exemple.café recusado extensão acentuada: ver os limites
a@b.c recusado extensão de uma só letra
@example.com recusado parte local vazia
jean@.com recusado domínio vazio antes da extensão
jean dupont@example.com recusado espaço
jean@example.com (espaço final) recusado a âncora $ recusa qualquer carácter depois da extensão: não te esqueças do trim()

O que nenhuma regex consegue verificar: que o domínio existe, que aceita correio e que a caixa está ativa. personne@gmail.com tem uma forma perfeita e muito provavelmente não recebe nada.

Versão JavaScript

A função recebe o endereço como parâmetro, retira os espaços à volta e devolve true ou false. A flag i torna a comparação insensível a maiúsculas e minúsculas.

validateEmail.js
/**
 * Verifica o formato de um endereço de email.
 * @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, graças ao trim

Num formulário

O caso de uso típico: bloquear o envio e mostrar uma mensagem enquanto o endereço estiver mal formado.

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

O campo de erro é um elemento visível ao lado do campo, não uma caixa alert(): o utilizador mantém o contexto e pode corrigir de imediato.

Versão PHP

A validação do lado do cliente é uma ajuda ao preenchimento, não uma medida de segurança. O servidor tem de repetir a verificação: o JavaScript pode ser desativado e um formulário pode ser chamado diretamente. A mesma regex funciona com preg_match(), tirando as aspas.

isValidEmail.php
/**
 * Verifica o formato de um endereço de email.
 */
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)

Repara no duplo escape das barras invertidas na string PHP entre aspas simples: \\\\ no código produz \\ no padrão, que significa «uma barra invertida literal» para o motor de regex.

Regex ou filter_var?

O PHP fornece filter_var($email, FILTER_VALIDATE_EMAIL), que segue a norma mais de perto. As duas abordagens são válidas; não decidem exatamente da mesma maneira:

Endereço Regex do artigo filter_var
a@b.c recusado aceite
"jean dupont"@example.com aceite recusado
jéan@exemple.fr aceite recusado sem FILTER_FLAG_EMAIL_UNICODE, aceite com
jean+news@sub.example.co.uk aceite aceite

Para um formulário público, filter_var com a flag Unicode é uma boa escolha por omissão; a regex continua útil quando queres exatamente a mesma regra em JavaScript e em PHP.

Versão HTML

O navegador já sabe validar um email: type="email" recusa o envio do formulário se o formato estiver errado e mostra a sua própria mensagem. O atributo pattern permite acrescentar uma regra mais estrita, a do artigo por exemplo.

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

Quatro pormenores: pattern está implicitamente ancorado, por isso não se escreve ^ nem $; as aspas do padrão escrevem-se &quot; para não fechar o atributo; os navegadores compilam pattern com a flag v, que obriga a escapar os parênteses e os parênteses retos dentro de uma classe de caracteres (\(\)\[\]), sob pena de o padrão ser ignorado em silêncio; e a validação HTML contorna-se com uma linha na consola. Melhora a experiência do utilizador, não substitui a verificação no servidor.

Casos-limite a conhecer

  • O +: jean+boutique@gmail.com é um endereço válido que muita gente usa para organizar o correio. Uma regex que o recuse perde inscrições.
  • As extensões longas: .photography, .paris e .technology existem. É por isso que o padrão termina em [a-zA-Z]{2,} e não numa lista fechada de extensões.
  • Os acentos: a parte local aceita-os; o domínio não os aceita nesta regex (os domínios internacionalizados escrevem-se, na realidade, em Punycode, xn--…, antes de serem resolvidos).
  • Os espaços à volta: copiar e colar a partir de uma folha de cálculo acrescenta muitas vezes um espaço final. Chama trim() antes do teste, como fazem as duas funções acima.
  • As maiúsculas: o domínio é insensível a maiúsculas e minúsculas; a parte local, em teoria, é sensível, mas nenhum fornecedor generalista faz a distinção. Comparar em minúsculas é o comportamento esperado.

Testar a regex antes de a pôr em produção

Um conjunto de testes vale mais do que uma releitura. Em JavaScript, um ciclo sobre endereços conhecidos chega:

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

Para explorar um padrão, o regex101 colore cada grupo e explica o que captura. Escolhe a variante ECMAScript para JavaScript e PCRE2 para PHP: ambas aceitam esta regex sem alterações.

Ir mais longe: verificar que o endereço existe

Quando a forma está correta, três verificações adicionais reduzem os endereços falsos, da menos dispendiosa à mais fiável:

  1. Recusar os domínios descartáveis (endereços temporários criados para contornar um registo) a partir de uma lista mantida.
  2. Consultar os registos MX do domínio no servidor: um domínio sem servidor de correio nunca receberá uma mensagem. Em PHP, checkdnsrr($domaine, 'MX') responde numa linha.
  3. Enviar um email de confirmação com uma ligação para clicar. É a única prova de que alguém lê aquela caixa, e é o que fazem todos os serviços sérios.

As duas primeiras verificações são as que aplico nos formulários públicos de Foliade; a terceira continua a ser a referência assim que se cria uma conta.

As outras expressões regulares de validação

Mesma abordagem, outros formatos: validar uma data, um endereço IP, um código postal ou uma palavra-passe.

Erros frequentes

Esquecer as âncoras ^ e $ Sem elas, test() aceita «bonjour jean@example.com merci» porque existe um endereço válido algures na string. A função deste artigo ancora a regex nas duas extremidades.
Validar apenas do lado do cliente O JavaScript desativa-se e contorna-se. A mesma verificação tem de existir no servidor, aqui em PHP, antes de guardar ou enviar seja o que for.
Achar que um endereço válido existe A regex verifica uma forma, não uma caixa de correio. «nimportequoi@gmail.com» passa. Só um email de confirmação prova que o endereço recebe correio.
Recusar endereços legítimos As regex demasiado estritas rejeitam o «+» (jean+newsletter@…) ou as novas extensões longas como .photography. Testa sempre com endereços reais antes de apertar a regra.

JavaScriptPHP

Damien Flandrin Programador web desde 2010, criador da Gekkode e do Email Impact. Cada artigo é testado num projeto real antes de ser publicado. Contacto
Newsletter

Os novos testes, tutoriais e projetos, por e-mail.

Testes reproduzíveis, código versionado, resultados datados. Nunca spam.