Skip to content
Empieza con 100 créditos de verificación gratis
Qualisend
Todos los artículos
Ingeniería / 12 de junio de 2026

Validar una dirección de correo en Laravel, de la regla al buzón

7 minutes read

Qualisend team
Ilustración de un array de reglas de Laravel que se estrecha desde email:rfc a email:rfc,dns hasta una regla DeliverableEmail que comprueba el buzón.

Validar una dirección de correo en Laravel invita a tratarlo como algo de una sola línea: añades email a un array de validación y a otra cosa. Esa regla es un buen punto de partida, pero solo responde a la primera de las tres preguntas que plantea una validación de verdad. ¿La dirección tiene la forma correcta? ¿Puede su dominio recibir correo? ¿Existe realmente el buzón? Laravel es peculiar en que su regla email integrada puede responder a las dos primeras en una sola línea; la tercera es un problema de red que conviene delegar. Esta guía construye cada capa sobre el propio validador de Laravel y muestra exactamente dónde se detiene el framework. Es una extensión de la guía de validación de correo en PHP: Laravel envuelve los mismos ladrillos de PHP —y filter_var está a una palabra de distancia— en una API mucho más agradable.

La respuesta corta#

Usa la regla email:rfc,dns de Laravel para la sintaxis y la consulta MX en una sola pasada, y luego una API de verificación para la comprobación SMTP del buzón: lo más barato primero, cortocircuitando con bail en cuanto una capa sea concluyente. No intentes abrir una sesión SMTP desde tu aplicación para sondear buzones tú mismo: el puerto 25 de salida está bloqueado en la mayoría de los alojamientos, y la respuesta depende de la reputación de la IP de envío y del greylisting que no quieres reimplementar. Cada capa descarta direcciones más barato que la anterior; solo la última puede dar por buena una dirección.

Capas 1 y 2: sintaxis y DNS en una sola regla#

La regla email se apoya en el paquete egulias/email-validator, y admite estilos que deciden cuán estricta es. Por defecto aplica el estilo rfc (gramática RFC 5321/5322). El que importa para la validación es dns, que añade una consulta en vivo y confirma que el dominio resuelve una ruta de correo —un registro MX, con un registro A como alternativa—. Pide ambos y Laravel hace la sintaxis y la comprobación del dominio en una única regla:

public function store(Request $request)
{
    $validated = $request->validate([
        'email' => ['required', 'email:rfc,dns'],
    ]);

    // $validated['email'] is well-formed AND its domain can receive mail.
}

Los demás estilos conviene conocerlos. email:filter ejecuta el filter_var($email, FILTER_VALIDATE_EMAIL) de PHP —la misma comprobación de la guía de PHP— y filter_unicode admite algunas partes locales en Unicode. strict rechaza las advertencias de puntos finales y consecutivos que el parser del RFC tolera. Y spoof protege frente a caracteres homógrafos engañosos, así que para un formulario de registro público email:rfc,dns,spoof es un valor por defecto sensato. En un form request queda así:

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 qué email:rfc,dns sigue sin ser luz verde#

Un aprobado en email:rfc,dns significa que la cadena está bien formada y que el dominio puede recibir correo. No dice nada sobre si el buzón concreto existe. noreply-9f2x@gmail.com pasa. Y también definitely-fake@gmail.com. Ambas son no entregables, porque el registro MX es un hecho sobre el dominio y el buzón es un hecho que solo puedes averiguar preguntándole al servidor de correo. Es el mismo muro que hace que la validación de correo por regex fracase: ninguna cantidad de coincidencia de patrones, ni ninguna consulta DNS, cruza de «el dominio acepta correo» a «esta dirección lo hace».

Si quieres ver la brecha por ti mismo, pega una dirección sintácticamente perfecta en el comprobador de correo gratuito y observa cómo una cadena aprobada por la regla vuelve como undeliverable.

Capa 3: ¿existe realmente el buzón?#

Confirmar un buzón concreto implica la conversación de entrega SMTP: conectar con el host de correo, emitir RCPT TO, leer la respuesta y desconectar antes de enviar nada. En principio podrías guionizar esto desde PHP, pero no deberías ejecutarlo desde tu servidor de aplicaciones: la mayoría de los proveedores en la nube bloquean el puerto 25 de salida, la respuesta depende de la reputación de la IP desde la que conectas, y los servidores receptores hacen greylisting y limitan el ritmo a remitentes desconocidos. Y hay más: cómo funciona la verificación de correo recorre todo el proceso, incluidos los dominios catch-all, que aceptan cualquier dirección y derrotan a un sondeo ingenuo. Esta es la capa que vale la pena delegar.

Llamar a Qualisend desde una regla de validación personalizada#

Laravel deja limpia la delegación: envuelve la llamada a la API en un objeto de regla y colócalo en el mismo array de reglas. Genera uno con Artisan:

php artisan make:rule DeliverableEmail

Mantén la clave fuera de tu código conectándola a través de config/services.php, que la lee de tu .env:

// config/services.php
'qualisend' => [
    'key' => env('QUALISEND_API_KEY'),
],

Entonces la regla llama al endpoint de verificación con la facade Http, lee el sobre result y falla solo cuando el veredicto es 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() fija la cabecera de autenticación bearer, ->post() envía el cuerpo JSON y $response->json('result') extrae el objeto result anidado con acceso por puntos. El endpoint de arriba es un marcador de posición —consulta la referencia de la API para la URL base y la forma exacta de la petición—, pero la respuesta se lee así:

{
  "result": {
    "status": "deliverable",
    "score": 95,
    "reason": null,
    "sub_flags": { "role": false, "disposable": false, "free": false, "catch_all": false }
  }
}

status es uno de deliverable, risky, undeliverable o unknown. La regla de arriba solo falla en firme ante undeliverable y deja pasar risky y unknown, para que decidas más adelante qué hacer con ellos —filtrar por score o ramificar según una entrada de sub_flags como disposable— en lugar de convertir una dirección dudosa en un error de formulario. Toma el JSON de arriba como la forma, no como el contrato; la lista completa de campos vive en la documentación para desarrolladores.

Uniendo las capas para validar una dirección de correo en Laravel#

Compón las tres en un solo form request, lo más barato primero, y deja que bail se detenga en el primer fallo, de modo que la regla de la API solo se ejecute sobre direcciones que ya han superado la sintaxis y la comprobación 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 es todo el truco. Una dirección mal formada o un dominio muerto falla en local y nunca gasta un crédito de la API; solo las direcciones que merecen el viaje de ida y vuelta por la red llegan a la capa SMTP. Indica el tipo del request en tu controlador y Laravel ejecuta el flujo antes de que el cuerpo de tu método llegue a ejecutarse:

public function store(StoreSubscriberRequest $request)
{
    // Every layer has passed — safe to persist.
    $subscriber = Subscriber::create($request->validated());

    return redirect()->route('subscribers.index');
}

Es la estratificación, no el framework, lo que hace fiable la validación —la misma forma que la versión en PHP, con Laravel colapsando las dos primeras capas en una sola regla—. Ejecuta el flujo síncrono y rápido en el registro y reserva la pasada más lenta y confirmada por SMTP para el trabajo de trastienda: la guía de registro serverless muestra el patrón en tiempo real, y cómo limpiar una lista de correo cubre la parte por lotes.

Preguntas frecuentes#

¿La regla email de Laravel comprueba si el dominio existe?#

Solo si se lo pides. La regla email a secas valida la sintaxis con el paquete egulias/email-validator (el estilo rfc). Añade el estilo dnsemail:rfc,dns— y Laravel también confirma que el dominio resuelve una ruta de correo, de modo que un dominio inventado o muerto falla en la misma pasada. Ningún estilo contacta con el buzón, así que un aprobado sigue significando «con pinta de entregable», no «entregable».

¿Cuál es la diferencia entre email:rfc, email:filter y email:dns?#

rfc valida contra los RFC 5321/5322 mediante egulias y es el valor por defecto de Laravel. filter ejecuta el filter_var($email, FILTER_VALIDATE_EMAIL) de PHP —la misma comprobación de la guía de PHP— mientras que filter_unicode admite algo de Unicode. dns añade una consulta MX en vivo, y spoof rechaza caracteres homógrafos engañosos. Los combinas con comas, así que email:rfc,dns es la puerta práctica de sintaxis más dominio.

¿Cómo verifico en Laravel que un buzón existe?#

No con una regla integrada: confirmar el buzón requiere una conversación SMTP que no deberías ejecutar desde tu servidor de aplicaciones, porque el puerto 25 está bloqueado en muchos sitios y el resultado depende de la reputación de tu IP de envío. Envuelve una API de verificación en un objeto ValidationRule personalizado, llámala con la facade Http, lee el sobre result y falla ante undeliverable. Colócala después de email:rfc,dns con bail para que solo se ejecute sobre direcciones que ya han superado las capas locales.

¿Ralentizará la regla dns el envío de mis formularios?#

Puede. El estilo dns realiza una consulta DNS en vivo en cada validación, así que un resolvedor lento o inaccesible atasca la petición. Está bien para un único formulario de registro, pero mantenlo fuera de las importaciones masivas y no lo apiles con una llamada síncrona a una API a menos que hayas fijado un tiempo de espera en el cliente Http, como hace la regla DeliverableEmail de arriba con ->timeout(10).


¿Listo para añadir la capa del buzón? Suelta una dirección en el comprobador de correo gratuito para ver cómo una dirección aprobada por la regla vuelve como no entregable, y luego conecta ese mismo veredicto en tu ValidationRule con los ejemplos listos para copiar y pegar de la referencia de la API.

Your reputation, protected.

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

Get started