An Email Regex That Actually Works (and When Not To)
Practical email regex patterns, their limitations, and when to use dedicated validation.
Email validation is one of the most common regex tasks, but it's also one of the most misunderstood. Understanding why perfect email regex is impossible and how to use pragmatic patterns helps you build better user experiences while avoiding performance pitfalls.
Why perfect email regex doesn't exist
The official email specification (RFC 5322) is extremely complex:
- Allows quoted strings:
"user name"@example.com - Supports comments:
user@example.com (John Doe) - Permits nested structures and edge cases
- Has thousands of valid formats
The problem: A regex that matches all valid emails would be:
- Thousands of characters long
- Extremely slow (catastrophic backtracking)
- Unreadable and unmaintainable
- Still wouldn't catch all edge cases
The solution: Use a simple, pragmatic pattern for UX validation, then verify emails through actual delivery (confirmation emails).
A pragmatic pattern that works
The recommended pattern
^[^\s@]+@[^\s@]+\.[^\s@]+$
Why this works:
- Simple - Easy to understand and maintain
- Fast - No catastrophic backtracking
- Good enough - Catches 99% of user errors
- Anchored -
^and$prevent partial matches
What it matches:
- ✅
user@example.com - ✅
user.name@example.co.uk - ✅
user+tag@example.com - ✅
user_name@sub.example.com
What it rejects:
- ❌
user@example(no TLD) - ❌
user @example.com(space) - ❌
user@@example.com(double @) - ❌
@example.com(no local part)
How the pattern works
Breaking it down:
^ # Start of string
[^\s@]+ # One or more characters that aren't whitespace or @
@ # Literal @ symbol
[^\s@]+ # One or more characters that aren't whitespace or @
\. # Literal dot (escaped)
[^\s@]+ # One or more characters that aren't whitespace or @
$ # End of string
Why [^\s@] instead of .:
.matches almost anything (causes backtracking)[^\s@]is specific (faster, clearer intent)- Prevents whitespace and multiple @ symbols
Testing the pattern
Use our Regex Tester to test this pattern:
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Valid emails
emailPattern.test('user@example.com'); // true
emailPattern.test('user.name@example.com'); // true
emailPattern.test('user+tag@example.co.uk'); // true
// Invalid emails
emailPattern.test('user@example'); // false (no TLD)
emailPattern.test('user @example.com'); // false (space)
emailPattern.test('user@@example.com'); // false (double @)
When to use stricter patterns
Pattern 1: Require TLD
If you want to ensure a top-level domain:
^[^\s@]+@[^\s@]+\.[^\s@]{2,}$
What changed:
[^\s@]{2,}- TLD must be at least 2 characters- Rejects
user@example.c(single char TLD)
Use when:
- You want to catch obvious typos
- TLD validation matters for your use case
Pattern 2: Limit local part length
If you want to enforce length limits:
^[^\s@]{1,64}@[^\s@]{1,255}\.[^\s@]{2,}$
What changed:
{1,64}- Local part 1-64 characters (RFC limit){1,255}- Domain 1-255 characters (RFC limit){2,}- TLD at least 2 characters
Use when:
- You need to enforce RFC length limits
- Database constraints require specific lengths
Pattern 3: Common domain validation
If you want to validate common domains:
^[^\s@]+@[^\s@]+\.(com|org|net|edu|gov|co\.uk|io|dev)$
What changed:
- Explicit TLD list
- More restrictive but catches typos
Use when:
- You only accept specific TLDs
- Corporate/internal email validation
Warning: This rejects valid international domains. Use with caution.
When NOT to use regex
Use dedicated libraries for:
1. Internationalized Domain Names (IDN)
- Emails like
用户@例子.测试 - Requires punycode conversion
- Use libraries:
validator.js,email-validator
2. Full RFC 5322 compliance
- Quoted strings:
"user name"@example.com - Comments:
user@example.com (John) - Complex edge cases
- Use libraries:
email-addresses,mailchecker
3. Domain existence verification
- Check if domain has MX records
- Verify domain actually exists
- Use DNS lookups, not regex
Example with library:
const validator = require('validator');
// More comprehensive validation
if (validator.isEmail(email)) {
// Email format is valid
}
// Check domain
if (validator.isEmail(email) && validator.isFQDN(email.split('@')[1])) {
// Email and domain format valid
}
Anti-patterns to avoid
Anti-pattern 1: Giant RFC 5322 regex
What developers try:
^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$
Why it's bad:
- Unreadable and unmaintainable
- Slow (complex backtracking)
- Still doesn't match all valid emails
- Hard to debug when it fails
Solution: Use simple pattern + verification
Anti-pattern 2: Overly permissive patterns
What developers try:
.*@.*
Why it's bad:
- Accepts
@(just @ symbol) - Accepts
user@(no domain) - Accepts
@example(no local part) - No validation value
Solution: Use anchored, specific pattern
Anti-pattern 3: Validating on every keystroke
What developers do:
// BAD - validates on every keystroke
input.addEventListener('input', () => {
if (!emailPattern.test(input.value)) {
showError('Invalid email');
}
});
Why it's bad:
- Poor UX (errors while typing)
- Performance overhead
- Frustrates users
Solution: Validate on blur or submit
Real-world implementation
Basic validation function
function isValidEmail(email) {
if (!email || typeof email !== 'string') {
return false;
}
// Trim whitespace
email = email.trim();
// Check basic format
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return pattern.test(email);
}
// Usage
if (isValidEmail(userInput)) {
// Proceed with signup
} else {
showError('Please enter a valid email address');
}
Enhanced validation with length checks
function isValidEmail(email) {
if (!email || typeof email !== 'string') {
return false;
}
email = email.trim();
// Basic format check
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!pattern.test(email)) {
return false;
}
// Length checks (RFC 5322 limits)
const [localPart, domain] = email.split('@');
if (localPart.length > 64 || domain.length > 255) {
return false;
}
return true;
}
Validation with domain whitelist
function isValidEmail(email, allowedDomains = null) {
if (!isValidEmailFormat(email)) {
return false;
}
// If domain whitelist provided, check it
if (allowedDomains && Array.isArray(allowedDomains)) {
const domain = email.split('@')[1];
return allowedDomains.includes(domain);
}
return true;
}
// Usage
const corporateEmail = isValidEmail(email, ['company.com', 'company.co.uk']);
UX best practices
1. Validate on blur, not per keystroke
Why: Better user experience, less frustration
Implementation:
// GOOD - validate on blur
emailInput.addEventListener('blur', () => {
if (!isValidEmail(emailInput.value)) {
showError('Please enter a valid email address');
} else {
clearError();
}
});
// BAD - validate on every keystroke
emailInput.addEventListener('input', () => {
// Shows errors while user is still typing
});
2. Don't block sign-up on regex alone
Why: Regex can't verify email actually works
Best practice:
- Validate format with regex (UX check)
- Send confirmation email
- Require email confirmation before full access
Implementation:
// Step 1: Format validation (regex)
if (!isValidEmail(email)) {
return { error: 'Invalid email format' };
}
// Step 2: Send confirmation email
await sendConfirmationEmail(email);
// Step 3: User confirms email
// Only then grant full access
3. Provide helpful error messages
Why: Users need to know what's wrong
Implementation:
function validateEmail(email) {
email = email.trim();
if (!email) {
return { valid: false, error: 'Email is required' };
}
if (!email.includes('@')) {
return { valid: false, error: 'Email must contain @ symbol' };
}
if (email.includes(' ')) {
return { valid: false, error: 'Email cannot contain spaces' };
}
const parts = email.split('@');
if (parts.length !== 2) {
return { valid: false, error: 'Email must have exactly one @ symbol' };
}
const [local, domain] = parts;
if (!local || !domain) {
return { valid: false, error: 'Email must have local and domain parts' };
}
if (!domain.includes('.')) {
return { valid: false, error: 'Email domain must contain a dot' };
}
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!pattern.test(email)) {
return { valid: false, error: 'Please enter a valid email address' };
}
return { valid: true };
}
Security considerations
1. Normalize Unicode homoglyphs
Problem: Attackers use look-alike characters
Example:
user@exаmple.com(Cyrillic 'а' instead of Latin 'a')user@exаmрle.com(mixed scripts)
Solution:
function normalizeEmail(email) {
// Convert to lowercase
email = email.toLowerCase();
// Normalize Unicode (NFKC)
email = email.normalize('NFKC');
// Remove homoglyphs (use library for production)
// Or whitelist specific domains
return email;
}
2. Log rejection reasons (without PII)
Why: Helps debug issues without exposing user data
Implementation:
function validateEmail(email) {
const result = isValidEmail(email);
if (!result.valid) {
// Log reason without email itself
logger.info('Email validation failed', {
reason: result.error,
domain: email.split('@')[1] ? 'present' : 'missing',
// Don't log full email (PII)
});
}
return result;
}
3. Rate limit validation attempts
Why: Prevents abuse and spam
Implementation:
const validationAttempts = new Map();
function validateEmailWithRateLimit(email, ipAddress) {
const key = `${ipAddress}:${email}`;
const attempts = validationAttempts.get(key) || 0;
if (attempts > 10) {
return { valid: false, error: 'Too many validation attempts' };
}
validationAttempts.set(key, attempts + 1);
// Clear after 1 hour
setTimeout(() => validationAttempts.delete(key), 3600000);
return isValidEmail(email);
}
Performance considerations
Why simple patterns are faster
Complex pattern:
^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@...
Performance:
- Slow on long strings
- Catastrophic backtracking risk
- Hard to optimize
Simple pattern:
^[^\s@]+@[^\s@]+\.[^\s@]+$
Performance:
- Fast (linear time)
- No backtracking issues
- Easy to optimize
Benchmark example:
const complexPattern = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+.../;
const simplePattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const testEmail = 'user@example.com';
console.time('complex');
complexPattern.test(testEmail);
console.timeEnd('complex'); // Slower
console.time('simple');
simplePattern.test(testEmail);
console.timeEnd('simple'); // Faster
Testing your pattern
Use our Regex Tester
Our Regex Tester helps you:
- Test patterns interactively
- See matches in real-time
- Identify performance issues
- Debug edge cases
Test cases to consider
Valid emails:
user@example.comuser.name@example.comuser+tag@example.comuser_name@example.comuser-name@example.comuser123@example.comuser@sub.example.comuser@example.co.uk
Invalid emails:
user@example(no TLD)user @example.com(space)user@@example.com(double @)@example.com(no local part)user@(no domain)user@.com(no domain name)user@example.(no TLD)
Best practices summary
- Use simple patterns -
^[^\s@]+@[^\s@]+\.[^\s@]+$for most cases - Validate on blur - Not on every keystroke
- Don't block on regex - Send confirmation emails
- Provide helpful errors - Tell users what's wrong
- Normalize input - Trim whitespace, handle Unicode
- Test thoroughly - Use our Regex Tester
- Use libraries for complex cases - IDN, full RFC compliance
- Log safely - Don't log full emails (PII)
When to use libraries instead
Use dedicated email validation libraries when:
- ✅ You need IDN support (international domains)
- ✅ You need full RFC 5322 compliance
- ✅ You need domain/MX record checking
- ✅ You're handling high-security scenarios
Popular libraries:
- JavaScript:
validator.js,email-validator - Python:
email-validator,validate_email - Java: Apache Commons Validator
Example with library:
const validator = require('validator');
// More comprehensive
if (validator.isEmail(email, {
allow_utf8_local_part: true,
require_tld: true
})) {
// Email is valid
}
Next steps
- Test email patterns with our Regex Tester
- Learn about regex performance traps to avoid slowdowns
- Master regular expressions basics for better patterns
- Understand lookaheads and lookbehinds for advanced validation
Try Regex Tester Now
Ready to put this into practice? Use our free Regex Tester tool. It works entirely in your browser with no signup required.
Launch Regex TesterFrequently Asked Questions
Q Should I validate emails with regex?
Use a simple regex for basic checks; rely on verification (e.g., confirmation emails) for accuracy.
Related Articles
Mastering Regular Expressions - Complete Guide to Regex Patterns
Learn everything about regular expressions, how they work, common patterns, and best practices for pattern matching, validation, and text processing.
Common Regex Performance Traps (and How to Avoid Them)
Avoid catastrophic backtracking and other performance killers with simple pattern tweaks.