E-mail e entregabilidade

Validação de e-mail: o que a regex prova e o que não prova

Quatro verificações diferentes são vendidas sob uma única palavra, ficam mais difíceis em ordem, e só a última estabelece o que você realmente queria saber. Aqui está o que um padrão consegue provar, o que uma consulta DNS acrescenta, por que perguntar ao servidor de e-mail vale menos do que parece, e qual das quatro é a única resposta de verdade.

  • Intermediário
  • 21 min de leitura
Um envelope azul flutuando ao lado de uma prancheta cinza marcada com um grande sinal de certo azul, com um cartão cinza menor marcado com um X inclinado à frente deles

Quatro perguntas que compartilham uma palavra

“Este é um endereço de e-mail válido?” não é uma pergunta só. São quatro, ficam mais difíceis em ordem, e a última é a única que alguém realmente queria ver respondida — por isso tanto código de validação é elaborado sobre a primeira e silencioso sobre o resto.

Aqui estão elas, na ordem em que um formulário de cadastro as encontra.

Tem o formato de um endereço?
Uma verificação de sintaxe. Roda no navegador, não custa nada, e pega a vírgula digitada por engano e o @ ausente. É a única camada que consegue rejeitar algo honestamente por conta própria — e mesmo assim deveria rejeitar bem menos do que a maioria dos padrões rejeita.
Aquele domínio consegue receber e-mail?
Uma verificação de DNS. Uma consulta diz se alguma coisa, em algum lugar, está disposta a aceitar e-mail para a parte depois do @. Ela pega um nome de domínio com uma letra faltando e um domínio que expirou no ano passado, e não consegue te dizer absolutamente nada sobre a caixa de entrada.
Aquela caixa de entrada existe?
Uma verificação SMTP — e a que este guia vai passar mais tempo dizendo para você não confiar nela. Dá para perguntar. A resposta é frequentemente um sim educado de um servidor que aceita qualquer nome, ou uma falha temporária proposital, ou uma aceitação seguida de um bounce minutos depois.
É o endereço dela?
Nada do lado técnico consegue responder isso. Um endereço pode ser perfeito, entregável e de outra pessoa — digitado errado por um caractere, ou digitado de propósito para passar por você. Só uma mensagem que chega e é usada resolve essa questão.
O padrãoRoda no navegadorO formato da stringNada sobre o mundonão custa nadaA consulta DNSMX, depois AO domínio recebeNada sobre a caixauma consultaA sondagem do servidorPergunta, depois desligaQuase nadaCatch-all diz sim a tudolenta, e arriscadaA mensagem confirmadaUm link, usado uma vezExiste alguém aliA única resposta que valeuma mensagem realCada linha custa mais do que a de cima, e vale mais.
As quatro camadas, e o que cada uma resolve. Só a linha de baixo produz um fato sobre a pessoa; as três acima dela reduzem o campo para que a mensagem valha a pena ser enviada.

As três primeiras são baratas e provam pouco. A quarta é a única que merece o nome, e é a que custa uma mensagem. Tudo abaixo é sobre gastar bem as três baratas para que a cara não seja desperdiçada.

Camada um: o padrão, e as quatro coisas que ele não pode saber

Não existe uma expressão regular oficial para um endereço de e-mail, e não pode existir. A RFC 5322 define uma gramática, não um padrão, e essa gramática permite comentários entre parênteses, espaços em branco quebrados e strings entre aspas contendo quase qualquer caractere — nada disso um formulário de cadastro de verdade deveria aceitar, e tudo isso um padrão fiel é obrigado a aceitar.

O que existe é uma definição deliberadamente mais restrita que os navegadores já implementam: a que a especificação HTML dá para um campo <input type="email">. É um meio-termo escolhido de propósito, não uma transcrição do padrão, é o que já se exige do seu formulário antes de qualquer linha do seu código rodar, e é curto o bastante para ler de uma vez:

regex
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/

Use essa, ou use a que a sua plataforma já traz pronta. As quatro abaixo são a mesma camada, e escolher uma é uma decisão menor do que o que você faz depois:

Onde você estáA verificação que você já temO que ela faz que talvez você não espere
No navegador, sem nenhum código<input type="email" required>Aplica o padrão acima antes de o seu JavaScript sequer ver o campo, e mostra a mensagem no idioma do próprio visitante.
Em PHP, sem instalar nadafilter_var($a, FILTER_VALIDATE_EMAIL)Mais rigorosa do que a do HTML: insiste em um ponto no domínio, então recusa um endereço que só funcionaria dentro de uma única máquina.
Em Python, com um pacote pequenoemail_validator.validate_email(a)Faz a sintaxe e, se você deixar, a consulta de domínio da camada dois — as duas camadas baratas atrás de uma única chamada.
Em Java ou Kotlin, por meio do Bean Validation@EmailMuito permissiva de propósito. É uma anotação feita para pegar o obviamente errado, e aceita sem problema nenhum um domínio que nunca existiu.

Qualquer uma que você use, o que importa é sobre o que ela fica em silêncio. Um padrão que retorna verdadeiro te contou uma coisa e quatro não-coisas.

Não que o domínio existe
Um endereço em um domínio que ninguém jamais registrou passa em todos os padrões desta seção. Nada foi consultado e nada foi contatado; nenhuma expressão regular jamais fez uma requisição de rede.
Não que a caixa de entrada existe
Mesmo em um domínio real, o padrão não tem opinião nenhuma sobre a parte antes do @. Essa parte pertence só ao servidor de recebimento, e é exatamente a coisa que o DNS nunca vai te contar.
Não que é entregável
Um domínio pode ter um registro perfeito e um servidor de e-mail que está desligado há um mês. Sintaxe é uma propriedade de uma string; capacidade de entrega é uma propriedade do mundo no momento em que você aperta enviar.
Não que pertence a quem está digitando
O endereço ruim mais comum em qualquer banco de dados é um endereço real, entregável, que pertence a alguém que nunca se cadastrou — porque uma letra foi digitada errado, ou porque o formulário foi preenchido com uma mentira que parecia plausível.

Camada dois: uma consulta, e o que ela resolve

A parte depois do @ é um domínio, e um domínio ou tem para onde colocar e-mail, ou não tem. Uma consulta DNS responde isso em poucos milissegundos, e é a verificação de maior valor deste guia inteiro — porque o erro que ela pega, um nome de domínio digitado um pouco errado, é de longe a falha real mais comum que existe.

shell
$ dig +short MX example.com

Uma resposta significa que um host foi nomeado. Nenhuma resposta não significa nenhum e-mail: um domínio sem registro MX mas com um registro A ainda recebe, porque remetentes recorrem a ele. Existem quatro resultados possíveis, e só dois deles são falhas.

O que a consulta retornaConsegue receber?O que o formulário deveria fazer
Um ou mais registros MXSimAceite. Esta é a esmagadora maioria dos endereços, e não vale a pena fazer mais nada antes de enviar.
Sem MX, mas com um registro A ou AAAASim, como alternativaAceite. Isso é incomum e totalmente legal, e remetentes vão entregar ali. Recusá-lo transforma um endereço que funciona em um cliente perdido.
Um único registro cujo valor inteiro é 0 .Não, e de propósitoRecuse, e diga o motivo. Isso é um MX nulo: o dono do domínio publicou, da única forma que existe para publicar isso, que nada ali recebe e-mail.
O domínio não resolve de jeito nenhumNãoRecuse, e ofereça a opção mais parecida. Um NXDOMAIN em um nome que um humano digitou há trinta segundos é quase sempre uma letra errada.

Onde essa verificação dá errado nunca é a consulta em si. É onde a consulta foi colocada — no caminho crítico, a cada tecla digitada, com uma falha que bloqueia o envio. Seis regras mantêm ela útil:

  1. Rode quando o campo estiver terminado, não enquanto ele está sendo digitado. Uma consulta quando o foco sai do campo, ou uma no envio. Uma consulta a cada tecla é uma consulta a cada tecla.
  2. Converta o domínio para minúsculas primeiro. O DNS não liga, mas o seu cache liga: duas grafias do mesmo domínio são uma consulta, não duas.
  3. Pergunte por MX, e recorra a A se precisar. Duas consultas, das quais só a segunda é condicional. Uma biblioteca que confere só o MX vai rejeitar domínios que funcionam.
  4. Guarde a resposta em cache por alguns minutos. Um punhado de domínios responde pela maioria dos cadastros em qualquer lugar, então a maioria das consultas vira consulta nenhuma.
  5. Falhe de forma permissiva. Se o resolvedor expirar, aceite o endereço. Um minuto ruim no seu provedor de DNS nunca deve virar um formulário que afasta todo mundo.
  6. Sugira uma correção; nunca aplique uma. Quando o domínio está a uma letra de um domínio comum, ofereça a correção como algo para clicar. Reescrever em silêncio o que alguém digitou é assim que um link de confirmação vai parar na mão de um estranho.

A consulta tem um custo que vale a pena conhecer: é o primeiro momento em que o seu formulário fala com o mundo exterior sobre algo que um visitante digitou. Onde isso importa, a alternativa é pular essa camada por completo e deixar a própria mensagem ser a verificação inteira.

Camada três: perguntar ao servidor, e por que a resposta não é uma resposta

Existe uma forma de perguntar a um servidor de e-mail se ele vai aceitar um endereço específico sem enviar nada para ele. Abra a conversa, nomeie um remetente, nomeie o destinatário, leia a resposta a esse único comando, e desligue antes da mensagem:

smtp
220 mx1.example.com ESMTP ready
EHLO checker.example.net
250 mx1.example.com
MAIL FROM:<probe@example.net>
250 2.1.0 Ok
RCPT TO:<someone@example.com>
250 2.1.5 Ok
QUIT
221 2.0.0 Bye

O 250 depois de RCPT TO é o que todo serviço de verificação de endereço no mundo está, no fundo, vendendo. Vale a pena ser preciso sobre o quão pouco isso vale.

Um domínio catch-all diz sim para tudo
Um domínio configurado para aceitar qualquer nome — que é o que este serviço faz, e o que uma quantidade enorme de domínios de empresas faz — responde 250 para um endereço que ninguém jamais usou. A resposta é verdadeira, e não é informação.
Um servidor cuidadoso diz 450 de propósito
Greylisting recusa a primeira tentativa de um remetente desconhecido e pede para ele voltar daqui a pouco. Um remetente de verdade volta; uma sondagem nunca volta. Essa falha temporária é uma não-resposta proposital, e interpretá-la como “essa caixa não existe” é exatamente o erro que ela foi projetada para provocar.
Alguns servidores aceitam primeiro e recusam depois
Grandes provedores rotineiramente pegam a mensagem durante a conversa e decidem sobre ela depois, o que transforma uma recusa em um bounce que chega minutos depois de a sua sondagem ter voltado limpa.
Alguns servidores dizem não a todo mundo que não reconhecem
Um servidor que decidiu que o seu endereço é um estranho pode recusar o destinatário por motivos que não têm nada a ver com o destinatário. O que você mediu foi a sua própria reputação, não a caixa de entrada dele.
E ela gasta a reputação que você estava protegendo
Uma conexão que nomeia destinatários e nunca envia nada tem exatamente o formato de uma coleta de diretório, porque é uma. Fazer isso em volume a partir do seu próprio endereço é o caminho mais rápido para uma lista de bloqueio — e um remetente bloqueado é aquele cujas mensagens de verdade param de chegar.

Existe um caso bem específico em que a sondagem é genuinamente útil: um único endereço, verificado manualmente, em um domínio que você administra ou tem permissão para mexer. Como uma etapa dentro de um formulário de cadastro, ela é lenta, frequentemente errada, e ocasionalmente prejudicial à própria coisa que foi adicionada para proteger.

Endereços de função, domínios descartáveis, e a lista que você está prestes a comprar

Entre a consulta de domínio e a mensagem de verdade fica uma família de verificações que não têm nada a ver com validade. Elas são sobre se você quer este endereço, o que é uma decisão de negócio vestida de decisão técnica — e vale a pena separá-la das três camadas ao redor só por esse motivo.

Endereços de função
Nomes como info@, support@ e admin@. Eles são reais, e geralmente são uma caixa de entrada compartilhada em vez de uma pessoa, o que os torna um mau lugar para qualquer coisa com senha por trás. Vale a pena sinalizar. Raramente vale a pena recusar.
Domínios descartáveis
Endereços como os que este site distribui, em domínios que existem para serem descartados. Conferir contra uma lista pública deles é razoável, desde que você admita que toda lista assim é incompleta e um pouco desatualizada no dia em que você a baixa.
Endereços de provedor gratuito
Alguns formulários corporativos recusam qualquer coisa que não seja um domínio de empresa. Isso é uma política, às vezes é a certa, e merece ser escrita como uma política — uma frase que o visitante consegue ler — em vez de escondida dentro de uma coisa chamada validação.
Domínios quase certos
O nome de um provedor conhecido com uma letra errada. Este é diferente dos outros três: não é uma política, pega um erro genuíno, e a pessoa sempre fica contente. É o único item aqui que vale a pena construir.

As três primeiras compartilham uma característica que torna difícil julgá-las: você consegue medir o que elas deixam passar e não consegue medir o que elas custam.

Nós temos um interesse óbvio aqui, então aqui está a versão honesta. Se o que está por trás do seu formulário é um teste grátis com algo caro atrelado, recusar domínios descartáveis vai economizar seu dinheiro e você deveria fazer isso. Se é uma newsletter, um download, ou uma conta que alguém paga, você está principalmente penalizando os cautelosos — e os cautelosos são os que leem o que você envia.

Camada quatro: a mensagem que é a verificação

Tudo acima reduz o campo. Nada disso estabelece o único fato que importa — que este endereço alcança a pessoa na sua frente — e exatamente uma coisa faz isso: enviar algo para lá, e observar se é usado.

Este é o loop que quase todo cadastro já tem e que metade deles trata como uma formalidade:

  1. Aceite o endereço com uma verificação permissiva. O padrão do navegador, e nada mais entre o visitante e o botão.
  2. Crie a conta não verificada. Nem uma retenção no formulário, nem uma tela de “pendente”: a pessoa já está dentro, e o que ela ainda não pode fazer é o punhado de coisas para as quais o endereço é realmente necessário.
  3. Envie uma mensagem carregando um link ou código de uso único. Uma só, com uma expiração, vinculada àquela conta e àquele endereço, e a nenhum outro.
  4. Deixe o link ser a prova. Um clique, ou um código digitado de volta, é a verificação inteira. Nada mais neste guia produz uma resposta nem perto de tão sólida.
  5. Dê a elas um caminho de volta. Um “reenviar” visível, e uma forma de mudar o endereço sem perder a conta — porque o motivo mais comum de um link nunca ser clicado é um erro de digitação que a pessoa agora finalmente consegue ver.
  6. Expire as que ninguém confirmou. Uma limpeza silenciosa depois de um período fixo impede que os erros de digitação e os endereços descartáveis se acumulem em uma lista em que ninguém confia.

O que cria um problema prático, e é o motivo pelo qual este site existe. Esse loop agora é o caminho mais importante do produto, e testá-lo significa receber e-mail de verdade em um endereço que você controla — repetidas vezes, dentro de um pipeline, sem um humano abrindo uma caixa de entrada.

Um endereço em um dos domínios públicos não precisa de cadastro nem de chave, e o que quer que chegue nele é legível por HTTP um segundo depois:

shell
$ curl -sG https://grabmail.io/api/v1/mailbox \
  --data-urlencode "address=signup-42@grabmail.io"

A partir daí, um fluxo de verificação inteiro pode ser conduzido de ponta a ponta por uma suíte de testes, ou um domínio que você possui pode ser apontado para cá para que cada execução ganhe um endereço que nunca existiu antes. As mensagens são mantidas por 5 dias e depois apagadas, o que é o tempo de vida certo para um fixture de teste e o errado para uma caixa de entrada.

O que realmente colocar no formulário

Resumido, na ordem em que o código roda:

  1. Corte os espaços, e só isso. Espaço em branco em qualquer ponta é um resíduo de colar e nunca é intencional. Mais nada na string é seu para mudar.
  2. Confira com um padrão permissivo. O do navegador, ou o da sua plataforma. Recuse o @ ausente e a vírgula digitada por engano, e não recuse mais nada.
  3. Limite o tamanho em 254. Um número, na coluna e no campo, e uma classe inteira de entrada com a qual você pode parar de se preocupar.
  4. Consulte o domínio, fora do caminho crítico, falhando de forma permissiva. MX e depois A, em cache, e nunca um motivo para bloquear um envio que de outra forma teria funcionado.
  5. Ofereça uma correção; não faça uma. Estar a uma letra de um domínio comum é uma pergunta a fazer, não uma constatação para agir sozinho.
  6. Envie a mensagem. Essa é a validação. Tudo acima disso foi triagem.
  7. Diga o que está errado, em palavras. Que é a parte que decide se alguém termina:
O que aconteceuO que formulários costumam dizerO que dizer em vez disso
A string não tem @ nela“Insira um endereço de e-mail válido”“Um endereço de e-mail precisa de um @ — você quis dizer name@example.com?”
O domínio não resolve“Insira um endereço de e-mail válido”“Não conseguimos encontrar esse domínio. A grafia está certa?”
O domínio está a uma letra de um domínio comumAbsolutamente nada; o formulário é enviado“Você quis dizer … ?”, com o endereço corrigido como um botão para clicar
Está em uma lista de bloqueio que você escolheu usar“Insira um endereço de e-mail válido”“Para isso precisamos de um endereço que você ainda vai conseguir ler no mês que vem.”
A mensagem foi enviada e nunca foi confirmadaAbsolutamente nada; a conta fica ali parada“Enviamos um link para esse endereço. Não chegou? Reenvie, ou mude o endereço.”

Repare quantas linhas dizem a mesma coisa errada. “Insira um endereço de e-mail válido” é o padrão em todo lugar porque é verdadeiro em todos os casos e útil em nenhum deles: em três das cinco linhas o endereço era válido, e a pessoa que o digitou não tem como descobrir em qual linha ela está.

A versão resumida

  1. Corte os espaços da string. Não mude mais nada nela.
  2. Confira com um padrão permissivo — o do navegador ou o da sua plataforma — e pare por aí. Não escreva o seu próprio.
  3. Rejeite qualquer coisa acima de 254 caracteres, e armazene uma coluna desse tamanho.
  4. Consulte o MX do domínio, recorra a A se precisar, guarde em cache, faça isso fora do caminho crítico, e aceite o endereço quando a consulta falhar.
  5. Recuse um domínio que não resolve e um domínio que publica um MX nulo. Aceite tudo o mais que o DNS te der.
  6. Ofereça uma sugestão de grafia quando o domínio estiver quase certo. Nunca a aplique sozinho.
  7. Decida separadamente, e por escrito, se você vai bloquear endereços descartáveis ou de função — e por quê.
  8. Envie uma mensagem com um link de uso único, trate o clique como a verificação, e facilite reenviar e corrigir o endereço.
  9. Teste esse loop contra uma caixa de entrada de verdade, incluindo os caminhos em que ele falha, e apague contas não confirmadas em um cronograma.

Nove linhas, e as duas últimas são as únicas que estabelecem alguma coisa. As outras sete existem para que a mensagem no final delas valha a pena ser enviada.

Perguntas

Existe uma expressão regular oficial para endereços de e-mail?

Não, e não pode existir. A RFC 5322 dá uma gramática, não um padrão, e um padrão fiel a ela aceitaria espaços entre aspas e comentários entre parênteses que nenhum provedor emite. Use a que a especificação HTML define para um campo de e-mail, ou qualquer verificador que a sua plataforma já traga pronto, e gaste o esforço que você economizou na mensagem de confirmação.

Dá para conferir se um endereço de e-mail existe sem enviar nada?

Dá para perguntar; não dá para saber. Um domínio catch-all aceita qualquer nome, greylisting responde com uma falha temporária proposital, grandes provedores aceitam durante a conversa e dão bounce depois, e um servidor que não te reconhece pode recusar por motivos que são sobre você, não sobre o destinatário. Sondar em massa também coloca o seu endereço de envio em listas de bloqueio.

name+tag@example.com é um endereço válido?

Sim. O sinal de mais é um caractere comum e permitido na parte local, e na maioria dos grandes provedores ele também roteia para a caixa de entrada antes dele, que é o que torna o endereçamento com sinal de mais útil. Um formulário que o recusa está recusando endereços legais e revelando algo sobre si mesmo ao fazer isso.

Endereços de e-mail diferenciam maiúsculas de minúsculas?

O domínio nunca diferencia. A parte antes do @, pelo padrão, fica a critério do servidor de recebimento — e na prática todo grande provedor ignora isso. Guarde uma cópia em minúsculas para detectar duplicatas, e envie para a string exatamente como foi digitada.

Qual é o tamanho máximo de um endereço de e-mail?

64 caracteres antes do @, 255 para o domínio, e 254 para o endereço inteiro conforme um servidor o transporta. O último desses três é o número para usar: coloque na coluna do banco de dados e no campo, e uma classe inteira de entrada deixa de ser problema seu.

Devo bloquear endereços de e-mail descartáveis?

Se há algo caro por trás de um teste grátis, sim — e aceite que qualquer lista que você use é incompleta. Para uma newsletter, um download ou uma conta paga, as pessoas que você afasta são principalmente as cautelosas, e você nunca vai vê-las em um log. A troca completa vale a pena ler antes de instalar um pacote que toma a decisão por você.

Como testar um fluxo de verificação de cadastro sem uma caixa de entrada de verdade?

Envie para um endereço em um domínio descartável público e leia de volta pela API — sem cadastro, sem chave, e um endereço novo a cada execução. Para uma suíte que precisa de centenas, aponte um domínio que você possui para uma caixa catch-all e invente um endereço por teste. O passo a passo completo está em testar um fluxo de verificação de e-mail de ponta a ponta.

Continue lendo

Experimente enquanto ainda está fresco

Um endereço leva um clique, sem conta e sem cartão. Tudo neste guia funciona nele imediatamente.

Bem-vindo de volta

As suas caixas e os seus domínios, num só lugar.