
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
^(([^<>()[\]\\.,;:\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.
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.
/**
* 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 trimNum formulário
O caso de uso típico: bloquear o envio e mostrar uma mensagem enquanto o endereço estiver mal formado.
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.
/**
* 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.
<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"
>Quatro pormenores: pattern está implicitamente ancorado, por isso não se escreve ^ nem $; as aspas do padrão escrevem-se " 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,.parise.technologyexistem. É 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:
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:
- Recusar os domínios descartáveis (endereços temporários criados para contornar um registo) a partir de uma lista mantida.
- 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. - 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
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.jean+newsletter@…) ou as novas extensões longas como .photography. Testa sempre com endereços reais antes de apertar a regra.

