/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ — this regex passes test@test.t and fails user+tag@subdomain.example.co.uk. Here's why email validation needs more than regex.
What Regex Gets Wrong
// This regex passes invalid addresses:
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(re.test('not-an-email@')); // false ✅
console.log(re.test('a@b.c')); // true — valid format, invalid TLD
console.log(re.test('test@[192.168.1.1]')); // false — actually valid per RFC 5321
console.log(re.test('"test test"@example.com')); // false — actually valid
And it can't catch:
- Disposable email addresses (Mailinator, Guerrilla Mail, etc.)
- Typos in the domain (
gmial.com,yhaoo.com) - Non-existent domains
- Full inboxes (will bounce)
Production-Grade Validation
import requests
def validate_email(email: str) -> dict:
resp = requests.get("https://api.lazy-mac.com/email-validator/check", params={
"email": email,
"check_mx": True, # Verify domain has mail server
"check_disposable": True, # Block throwaway addresses
"suggest_correction": True # Catch typos
})
return resp.json()
result = validate_email("user@gmial.com")
# {
# "valid": false,
# "reason": "domain_not_found",
# "suggestion": "user@gmail.com",
# "disposable": false,
# "mx_found": false
# }
The Validation Funnel
Run these checks in order (cheapest to most expensive):
- Format check (regex) — free, instant
- Domain existence (DNS lookup) — fast, catches typos
- MX record check (DNS) — catches "no mail server"
- Disposable email list — catches throwaway addresses
- SMTP ping (optional) — most accurate, but slow and often blocked
For signup forms: steps 1-4 are the sweet spot.
The Business Case
A 10% bounce rate on your email list:
- Triggers spam filters for all your emails
- Reduces deliverability for your entire domain
- Can get your SendGrid/Mailgun account suspended
One-time validation at signup pays for itself.
Top comments (0)