Validar um endereço de e-mail no Laravel dá vontade de tratar como uma linha só —
adicionar email a um array de validação e seguir em frente. Essa regra é um bom
começo, mas responde apenas à primeira das três perguntas que a validação de
verdade faz. O endereço tem o formato correto? O domínio dele consegue receber
e-mail? A caixa de entrada realmente existe? O Laravel é peculiar porque sua regra
email nativa consegue responder às duas primeiras em uma única linha; a
terceira é um problema de rede que vale a pena delegar. Este guia constrói cada
camada sobre o próprio validador do Laravel e mostra exatamente onde o framework
para. Ele amplia o
guia de validação de e-mail em PHP: o Laravel
envolve os mesmos blocos de construção do PHP — e filter_var está a uma palavra-chave
de distância — em uma API muito mais agradável.
A resposta curta#
Use a regra email:rfc,dns do Laravel para a sintaxe e a consulta MX em uma única
passagem e, depois, uma API de verificação para a checagem SMTP da caixa de
entrada — o mais barato primeiro, encerrando cedo com bail assim que uma camada
for decisiva. Não tente abrir uma sessão SMTP a partir da sua aplicação para
sondar caixas de entrada por conta própria: a porta 25 de saída é bloqueada na
maioria dos hosts, e a resposta depende da reputação do IP de envio e do
greylisting que você não vai querer reimplementar. Cada camada elimina endereços
de forma mais barata que a anterior; só a última consegue aprovar um endereço.
Camadas 1 e 2: sintaxe e DNS em uma só regra#
A regra email é sustentada pelo pacote egulias/email-validator, e ela aceita
estilos que decidem o quão rígida ela é. Por padrão, aplica o estilo rfc
(gramática das RFC 5321/5322). O estilo que importa para a validação é o dns,
que adiciona uma consulta ao vivo e confirma que o domínio resolve uma rota de
e-mail — um registro MX, com um registro A como alternativa. Peça os dois e o
Laravel faz a sintaxe e a checagem de domínio em uma única regra:
public function store(Request $request)
{
$validated = $request->validate([
'email' => ['required', 'email:rfc,dns'],
]);
// $validated['email'] is well-formed AND its domain can receive mail.
}
Os outros estilos vale a pena conhecer. email:filter executa o
filter_var($email, FILTER_VALIDATE_EMAIL) do PHP — exatamente a checagem do
guia de PHP — e filter_unicode permite
algumas partes locais em Unicode. strict rejeita os avisos de ponto final e
pontos consecutivos que o parser da RFC tolera. E spoof protege contra
caracteres homógrafos enganosos, então, para um formulário de cadastro público,
email:rfc,dns,spoof é um padrão sensato. Em um form request fica assim:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreSubscriberRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => ['required', 'email:rfc,dns,spoof'],
];
}
}
Por que email:rfc,dns ainda não é sinal verde#
Passar em email:rfc,dns significa que a string está bem formada e que o
domínio consegue receber e-mail. Não diz nada sobre a existência daquela caixa de
entrada específica. noreply-9f2x@gmail.com passa. definitely-fake@gmail.com
também. Ambos são inentregáveis, porque o registro MX é um fato sobre o domínio
e a caixa de entrada é um fato que você só descobre perguntando ao servidor de
e-mail. Essa é a mesma parede que faz a
validação de e-mail por regex falhar:
nenhuma dose de correspondência de padrões, e nenhuma consulta DNS, atravessa de
"o domínio aceita e-mail" para "este endereço aceita".
Se quiser ver a diferença você mesmo, cole um endereço sintaticamente perfeito no
verificador de e-mail gratuito e observe uma string
aprovada pela regra voltar como undeliverable.
Camada 3: a caixa de entrada realmente existe?#
Confirmar uma caixa de entrada específica significa a conversa de entrega SMTP:
conectar ao host de e-mail, emitir RCPT TO, ler a resposta e desconectar antes
de enviar qualquer coisa. Em princípio, você poderia programar isso em PHP, mas
não deveria executá-lo a partir do seu servidor de aplicação — a maioria dos
provedores de nuvem bloqueia a porta 25 de saída, a resposta depende da reputação
do IP de onde você conecta, e os servidores de recebimento fazem greylisting e
limitam a taxa de remetentes desconhecidos. E tem mais:
como funciona a verificação de e-mail
percorre o pipeline completo, incluindo domínios catch-all que aceitam todo
endereço e derrotam uma sondagem ingênua. Esta é a camada que vale a pena delegar.
Chamando a Qualisend a partir de uma regra de validação personalizada#
O Laravel deixa a delegação limpa: envolva a chamada de API em um objeto de regra e coloque-o no mesmo array de regras. Gere um com o Artisan:
php artisan make:rule DeliverableEmail
Mantenha a chave fora do seu código configurando-a através de config/services.php,
que a lê do seu .env:
// config/services.php
'qualisend' => [
'key' => env('QUALISEND_API_KEY'),
],
Então a regra chama o endpoint de verificação com a facade Http, lê o envelope
result e falha apenas quando o veredito é undeliverable:
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Http;
class DeliverableEmail implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$response = Http::withToken(config('services.qualisend.key'))
->timeout(10)
->post('https://api.qualisend.com/v1/verify', ['email' => $value]);
// On our own outage or a rate limit, don't block a real signup.
if ($response->failed()) {
return;
}
$result = $response->json('result');
if (($result['status'] ?? null) === 'undeliverable') {
$fail('This email address appears to be undeliverable.');
}
}
}
Http::withToken() define o cabeçalho de autenticação bearer, ->post() envia o
corpo JSON, e $response->json('result') extrai o objeto result aninhado com
acesso por ponto. O endpoint acima é um placeholder — consulte a
referência da API para a URL base e o formato de requisição exatos —
mas a resposta é lida de volta assim:
{
"result": {
"status": "deliverable",
"score": 95,
"reason": null,
"sub_flags": { "role": false, "disposable": false, "free": false, "catch_all": false }
}
}
status é um dentre deliverable, risky, undeliverable ou unknown. A regra
acima falha de forma dura apenas em undeliverable e deixa risky e unknown
passarem, para que você decida mais adiante o que fazer com eles — condicionar
pelo score, ou ramificar em uma entrada de sub_flags como disposable — em
vez de transformar um endereço limítrofe em um erro de formulário. Trate o JSON
acima como o formato, não como o contrato; a lista completa de campos vive na
documentação para desenvolvedores.
Juntando as camadas para validar um endereço de e-mail no Laravel#
Componha as três em um único form request, o mais barato primeiro, e deixe o
bail parar na primeira falha para que a regra de API só rode em endereços que já
passaram pela sintaxe e pela checagem MX:
namespace App\Http\Requests;
use App\Rules\DeliverableEmail;
use Illuminate\Foundation\Http\FormRequest;
class StoreSubscriberRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => [
'bail',
'required',
'email:rfc,dns',
new DeliverableEmail(),
],
];
}
public function messages(): array
{
return [
'email.email' => 'Enter a valid email address on a real domain.',
];
}
}
bail é o truque todo. Um endereço malformado ou um domínio inativo falha
localmente e nunca gasta um crédito de API; só os endereços que valem a ida e
volta pela rede chegam à camada SMTP. Faça o type-hint do request no seu
controller e o Laravel roda o pipeline antes mesmo de o corpo do seu método
executar:
public function store(StoreSubscriberRequest $request)
{
// Every layer has passed — safe to persist.
$subscriber = Subscriber::create($request->validated());
return redirect()->route('subscribers.index');
}
É a estratificação, não o framework, que torna a validação confiável — o mesmo formato da versão em PHP, com o Laravel colapsando as duas primeiras camadas em uma só regra. Rode o pipeline síncrono rápido no cadastro e reserve a passagem mais lenta, confirmada por SMTP, para o trabalho de bastidores: o guia de cadastro serverless mostra o padrão em tempo real, e como limpar uma lista de e-mails cobre o lado do processamento em lote.
Perguntas frequentes#
A regra email do Laravel verifica se o domínio existe?#
Só se você pedir. A regra email simples valida a sintaxe com o pacote
egulias/email-validator (o estilo rfc). Adicione o estilo dns —
email:rfc,dns — e o Laravel também confirma que o domínio resolve uma rota de
e-mail, então um domínio inventado ou inativo falha na mesma passagem. Nenhum dos
estilos contata a caixa de entrada, então uma aprovação ainda significa "com cara
de entregável", não "entregável".
Qual é a diferença entre email:rfc, email:filter e email:dns?#
rfc valida contra as RFC 5321/5322 via egulias e é o padrão do Laravel.
filter executa o filter_var($email, FILTER_VALIDATE_EMAIL) do PHP — exatamente
a checagem do guia de PHP — enquanto filter_unicode permite algum Unicode. dns
adiciona uma consulta MX ao vivo, e spoof rejeita caracteres homógrafos
enganosos. Você os combina com vírgulas, então email:rfc,dns é o portão prático
de sintaxe mais domínio.
Como verifico se uma caixa de entrada existe no Laravel?#
Não com uma regra nativa — a confirmação da caixa de entrada exige uma conversa
SMTP que você não deveria executar a partir do seu servidor de aplicação, porque a
porta 25 é amplamente bloqueada e o resultado depende da reputação do seu IP de
envio. Envolva uma API de verificação em um objeto ValidationRule personalizado,
chame-a com a facade Http, leia o envelope result e falhe em undeliverable.
Coloque-a depois de email:rfc,dns com bail para que ela só rode em endereços
que já passaram pelas camadas locais.
A regra dns vai deixar o envio dos meus formulários mais lento?#
Pode deixar. O estilo dns realiza uma consulta DNS ao vivo a cada validação,
então um resolvedor lento ou inacessível trava a requisição. Tudo bem para um
único formulário de cadastro, mas mantenha-o fora de importações em massa, e não o
empilhe com uma chamada de API síncrona a menos que você tenha definido um timeout
no cliente Http — como a regra DeliverableEmail acima faz com ->timeout(10).
Pronto para adicionar a camada da caixa de entrada? Jogue um endereço no
verificador de e-mail gratuito para ver um endereço
aprovado pela regra voltar como inentregável, e depois conecte o mesmo veredito à
sua ValidationRule com os exemplos de copiar e colar da
referência da API.