Por qué la validación de email es más difícil de lo que crees

El formato de dirección de email está definido por RFC 5322 y sus predecesores. La especificación completa permite algunas direcciones de aspecto sorprendentemente extraño que son técnicamente válidas:

  • "spaces in quotes"@example.com — cadenas entre comillas en la parte local
  • user+tag@example.com — direccionamiento plus (ampliamente usado para filtros en Gmail)
  • user@subdomain.example.co.uk — múltiples subdominios y ccTLDs
  • user@[192.168.1.1] — literales de dirección IP como dominio
  • very.unusual."@".unusual.com@example.com — técnicamente válido pero casi nunca visto
  • user@xn--nxasmq6b.com — nombres de dominio internacionalizados (Punycode)

Un regex completamente compatible con RFC 5322 necesitaría manejar todos estos, mientras también rechaza direcciones que no cumplen las reglas básicas de formato. El resultado es un patrón de 1000 caracteres que es esencialmente imposible de mantener. En la práctica, el objetivo no es la compatibilidad total con el RFC — es detectar errores tipográficos sin bloquear direcciones reales.

El regex simple (suficiente para el 99% de los casos)

Para la mayoría de las aplicaciones, este patrón mínimo es la elección correcta. Valida la estructura esencial — algo antes de un @, un dominio y un TLD — sin falsos negativos en direcciones legítimas:

/^[^\s@]+@[^\s@]+\.[^\s@]+$/

Desglose:

  • ^ — inicio de la cadena
  • [^\s@]+ — uno o más caracteres que no son espacio en blanco ni @ (la parte local)
  • @ — arroba literal
  • [^\s@]+ — uno o más caracteres que no son espacio en blanco ni @ (el dominio)
  • \. — un punto literal
  • [^\s@]+ — uno o más caracteres (el TLD)
  • $ — fin de la cadena

Lo que acepta y rechaza correctamente:

Válidos (aceptados):

✓ user@example.com
✓ first.last@company.co.uk
✓ user+tag@gmail.com
✓ 123@numbers.org

Inválidos (rechazados):

✗ plainstring
✗ @nodomain.com
✗ user @example.com (espacio)
✗ user@

La principal debilidad es que acepta user@@example.com y a@b.c (técnicamente válido pero sospechoso). Para la mayoría de formularios de registro e inputs de API, esta es una compensación aceptable.

El regex completo (compatible con RFC 5322)

Cuando necesitas una validación más estricta — por ejemplo, en un sistema de envío de emails donde las direcciones inválidas causan rebotes y dañan la reputación del remitente — un patrón más riguroso está justificado. Este es un patrón derivado de RFC 5322 ampliamente citado:

/^(([^<>()\[\]\\.,;:\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,}))$/

Este patrón es más largo pero maneja significativamente más casos. Desglose de las partes clave:

  • Parte local (antes de @): Ya sea una secuencia de caracteres permitidos con segmentos opcionales separados por puntos [^<>()...]+(\.[^<>()...]+)*, o una cadena entre comillas ".+" para direcciones como "john doe"@example.com.
  • Dominio (después de @): Ya sea un literal de dirección IP entre corchetes \[[0-9]{1,3}...\], o un nombre de host estándar con un TLD de al menos 2 caracteres ([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}.
  • TLD mínimo de 2 caracteres: Rechaza TLDs de un solo carácter mientras acepta los largos como .museum o .technology.

Casos adicionales que este patrón maneja correctamente:

✓ "quoted string"@example.com
✓ user@[192.168.1.1]
✓ user@subdomain.long-domain.co.uk

Aún rechazados correctamente:

✗ user@example (sin TLD)
✗ user @example.com (espacio antes de @)
✗ user@@example.com (doble @)

Validación de email en JavaScript

Aquí hay implementaciones prácticas en JavaScript listas para copiar y pegar usando ambos patrones:

Función de validación simple

/**
 * Valida formato de email para la mayoría de casos de uso.
 * Rápido, legible, muy pocos falsos negativos.
 */
function isValidEmail(email) {
  const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return pattern.test(email.trim());
}

// Uso
isValidEmail("user@example.com");      // true
isValidEmail("not-an-email");          // false
isValidEmail("user+tag@gmail.com");    // true

Validación más estricta con retroalimentación detallada

function validateEmail(email) {
  const trimmed = email.trim();

  if (!trimmed) {
    return { valid: false, error: "El email es requerido" };
  }

  if (trimmed.length > 254) {
    return { valid: false, error: "Dirección de email muy larga (máximo 254 caracteres)" };
  }

  if (!trimmed.includes("@")) {
    return { valid: false, error: "Falta el símbolo @" };
  }

  const [local, ...domainParts] = trimmed.split("@");
  const domain = domainParts.join("@");

  if (!local || local.length > 64) {
    return { valid: false, error: "Parte local inválida (antes de @)" };
  }

  if (!domain || !domain.includes(".")) {
    return { valid: false, error: "Dominio inválido (falta TLD)" };
  }

  const rfcPattern = /^(([^<>()\[\]\\.,;:\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,}))$/;

  if (!rfcPattern.test(trimmed)) {
    return { valid: false, error: "Formato de email inválido" };
  }

  return { valid: true, email: trimmed.toLowerCase() };
}

// Uso
const result = validateEmail("  User@Example.COM  ");
// { valid: true, email: "user@example.com" }

Validación de email con HTML5

Antes de recurrir a JavaScript, considera lo que el navegador te da gratis. El elemento input type="email" tiene validación incorporada que se ejecuta al enviar el formulario:

<!-- Validación incorporada básica -->
<input type="email" name="email" required>

<!-- Con patrón personalizado para validación más estricta -->
<input
  type="email"
  name="email"
  required
  pattern="[^\s@]+@[^\s@]+\.[^\s@]+"
  title="Por favor ingresa una dirección de email válida"
>

El algoritmo incorporado del navegador para type="email" sigue la especificación HTML5 (un subconjunto simplificado de RFC 5322). Rechaza no-emails obvios, muestra un tooltip de validación nativo y funciona sin JavaScript. El atributo pattern permite agregar restricciones adicionales.

Limitaciones a tener en cuenta: la validación incorporada solo se activa al enviar el formulario, no mientras el usuario escribe. Para retroalimentación en tiempo real, necesitas JavaScript. Además, la pseudo-clase CSS :invalid permite estilizar inputs inválidos, pero se activa incluso en campos vacíos no tocados a menos que también uses :not(:placeholder-shown):

/* Solo mostrar borde rojo después de que el usuario haya interactuado */
input[type="email"]:not(:placeholder-shown):invalid {
  border-color: #ef4444;
  outline-color: #ef4444;
}

input[type="email"]:not(:placeholder-shown):valid {
  border-color: #34d399;
}

Casos extremos comunes

Estos son los casos que hacen fallar a la mayoría de implementaciones de validación de email:

Direccionamiento plus (subaddressing)

user+tag@gmail.com es válido y ampliamente usado. Gmail, Outlook y la mayoría de proveedores de email modernos lo soportan para filtrado. Tu regex no debe rechazar el carácter + en la parte local. Muchos patrones excesivamente restrictivos fallan en esto.

Subdominios

first.last@mail.company.co.uk es un email válido con puntos tanto en la parte local como en el dominio. El dominio tiene tres niveles. Un buen patrón debe manejar múltiples etiquetas separadas por puntos en el dominio.

TLDs largos

Los TLDs ya no están restringidos a dos o tres caracteres. .museum, .photography, .technology y cientos de otros gTLDs son válidos. Cualquier regex que imponga una longitud máxima de TLD de 4 o 6 caracteres producirá falsos negativos. Impón solo un mínimo (2 caracteres) y ningún máximo.

Dominios internacionales (IDN)

Los nombres de dominio pueden contener caracteres no ASCII codificados como Punycode. user@münchen.de es válido — el dominio real es xn--mnchen-3ya.de en forma ASCII. La mayoría de los patrones regex no pueden validar Punycode directamente; confía en una librería dedicada de validación de email si se requiere soporte IDN.

Literales de dirección IP

user@[192.168.1.1] es técnicamente válido según RFC 5321 pero casi nunca se usa en la práctica. A menos que estés construyendo una implementación SMTP, puedes ignorar este caso de forma segura.

No sobre-valides

Este es el consejo práctico más importante de este artículo: el propósito de la validación de email del lado del cliente es detectar errores tipográficos, no verificar la capacidad de entrega.

Ningún regex puede decirte si una dirección de email realmente existe o si su bandeja de entrada está aceptando correo. Solo enviar al buzón puede confirmar eso. Sobre-validar (rechazar direcciones que tu regex marca incorrectamente como inválidas) pierde usuarios reales. Sub-validar (aceptar direcciones mal formadas) te cuesta un rebote.

El enfoque correcto es una estrategia de dos capas:

  1. Usa un regex simple para detectar errores obvios de formato (falta el @, falta el TLD, espacios).
  2. Envía un email de confirmación o verificación para confirmar que la dirección es real y el usuario la controla.

Un email de confirmación es la única puerta confiable. Todo lo demás es una heurística. Si te encuentras debatiendo si rechazar user@localhost o a@b.io, da un paso atrás — envía el email y deja que la capa SMTP lo maneje.

Comparación de patrones regex populares para email

Aquí hay una comparación práctica de los patrones más comúnmente usados, incluyendo sus compensaciones:

Patrón Tipo Ventajas Desventajas Cobertura
/^[^\s@]+@[^\s@]+\.[^\s@]+$/ Simple Legible, mínimos falsos negativos, rápido Acepta a@@b.c, muy permisivo ~95%
/^[\w.+-]+@[\w-]+\.[\w.]{2,}$/ Común Corto, maneja la mayoría de emails del mundo real Rechaza caracteres especiales válidos en la parte local ~92%
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ Equilibrado Ampliamente usado, buen equilibrio de rigurosidad y cobertura Rechaza algunos locales válidos con cadenas entrecomilladas ~97%
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}...)/ RFC 5322 Maneja cadenas entrecomilladas, literales IP, TLDs largos Largo, difícil de leer, aún no 100% compatible con RFC ~99%

El patrón "equilibrado" completo

El tercer patrón en la tabla anterior es la elección más práctica para uso en producción cuando quieres más que el patrón simple pero no la complejidad completa de RFC 5322:

/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

Desglose:

  • [a-zA-Z0-9._%+-]+ — parte local: alfanuméricos más ., _, %, +, -
  • @ — arroba requerida
  • [a-zA-Z0-9.-]+ — dominio: alfanuméricos, guiones, puntos (maneja subdominios)
  • \. — punto separador antes del TLD
  • [a-zA-Z]{2,} — TLD: al menos 2 letras, sin máximo (maneja TLDs largos)

Usar una librería en lugar de crear la tuya propia

Para aplicaciones donde la validez del email es crítica (sistemas de email transaccional, flujos de registro B2B SaaS), considera una librería de validación dedicada en lugar de escribir regex tú mismo:

// Node.js — validator.js (la más popular)
import validator from 'validator';
validator.isEmail('user@example.com'); // true

// Node.js — email-validator (ligera)
import { validate } from 'email-validator';
validate('user@example.com'); // true

// Python — email-validator (compatible con RFC)
# pip install email-validator
from email_validator import validate_email, EmailNotValidError
try:
    info = validate_email("user@example.com")
except EmailNotValidError as e:
    print(str(e))

Prueba tu regex de email

La mejor forma de entender cualquier regex es ejecutarlo contra un conjunto completo de entradas de prueba — tanto direcciones válidas que esperas aceptar como inválidas que necesitas rechazar. Nuestro Regex Tester te permite pegar cualquiera de los patrones anteriores, construir un conjunto de pruebas y ver resultados de coincidencia resaltados en tiempo real.

Prueba tu regex de email en vivo

Pega cualquier patrón regex de email y pruébalo contra tus propias entradas. Soporta flags, grupos de captura y matching multilínea. Se ejecuta completamente en tu navegador.

Abrir Regex Tester →

Herramientas de desarrollo relacionadas