X Xerobit

Email Regex Validation — How to Validate Email Addresses

Email validation with regex catches most invalid addresses but can't verify deliverability. Here's the right regex for email validation, edge cases to handle, and why you...

Mian Ali Khalid · · 5 min read
Use the tool
Email & URL Extractor
Extract every email address and URL from a block of text. Regex-based, case-insensitive, deduplicated, sorted output.
Open Email & URL Extractor →

Email validation with regex is one of the most common programming tasks — and also one of the most frequently done incorrectly. A regex that’s too strict rejects valid addresses. A regex that’s too permissive lets garbage through. Here’s the right approach.

Use the Email Extractor to find and extract email addresses from text.

The right validation strategy

Regex for format checking + verification for deliverability.

A regex can only check that an email address looks valid. It can’t tell you if:

  • The domain exists
  • The mailbox actually accepts mail
  • The address isn’t a disposable/throwaway address

The only reliable way to verify an email is to send a confirmation email and require the user to click a link.

A practical email regex

The most commonly used pattern (covers 99.9% of real emails):

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

Breaking it down:

  • ^ — start of string
  • [^\s@]+ — one or more characters that aren’t whitespace or @
  • @ — literal @ symbol
  • [^\s@]+ — domain name (one or more non-whitespace, non-@ chars)
  • \. — literal dot
  • [^\s@]+$ — TLD (one or more non-whitespace, non-@ chars)
  • $ — end of string

This matches:

✓ user@example.com
✓ user+tag@example.com
✓ first.last@company.co.uk
✓ user123@domain.io
✓ USER@EXAMPLE.COM
✗ @example.com (no local part)
✗ user@example (no TLD)
✗ user @example.com (space)
✗ user@.com (empty domain)

More thorough validation regex

For stricter (but not excessive) validation:

/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/

This follows RFC 5322 more closely and validates:

  • Local part: allows all RFC-permitted special characters
  • Domain: each label max 63 chars, starts/ends with alphanumeric
  • TLD: must be 2+ alphabetic characters

JavaScript implementation

HTML5 input validation (simplest approach)

<input type="email" name="email" required>

The browser validates the format. No JavaScript needed for basic validation.

JavaScript regex

function isValidEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

// Usage:
console.log(isValidEmail('user@example.com'));    // true
console.log(isValidEmail('user+tag@example.com')); // true
console.log(isValidEmail('@example.com'));         // false
console.log(isValidEmail('notanemail'));            // false

// With normalization (trim whitespace, lowercase):
function validateEmail(email) {
  const normalized = email.trim().toLowerCase();
  const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized);
  return { valid, normalized };
}

Form validation with feedback

function validateEmailField(input) {
  const value = input.value.trim();
  const errorEl = document.getElementById(`${input.id}-error`);
  
  if (!value) {
    showError(errorEl, 'Email address is required');
    return false;
  }
  
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
    showError(errorEl, 'Please enter a valid email address');
    return false;
  }
  
  clearError(errorEl);
  return true;
}

function showError(el, message) {
  el.textContent = message;
  el.style.display = 'block';
}

function clearError(el) {
  el.textContent = '';
  el.style.display = 'none';
}

Python validation

import re

EMAIL_REGEX = re.compile(r'^[^\s@]+@[^\s@]+\.[^\s@]+$')

def is_valid_email(email: str) -> bool:
    return bool(EMAIL_REGEX.match(email.strip()))

# Using email-validator library (more robust):
from email_validator import validate_email, EmailNotValidError

def validate_email_address(email: str) -> dict:
    try:
        info = validate_email(email, check_deliverability=False)
        return {
            'valid': True,
            'normalized': info.normalized,
            'local': info.local_part,
            'domain': info.domain,
        }
    except EmailNotValidError as e:
        return {'valid': False, 'error': str(e)}

# With DNS check (verifies domain exists):
def verify_email_domain(email: str) -> dict:
    try:
        info = validate_email(email, check_deliverability=True)  # Does DNS lookup
        return {'valid': True, 'normalized': info.normalized}
    except EmailNotValidError as e:
        return {'valid': False, 'error': str(e)}

Edge cases in email addresses

These are all valid email addresses that strict regex often rejects:

# Plus addressing (widely used):
user+newsletter@gmail.com
alice+github@company.com

# Dots in local part:
first.last@example.com
a.b.c.d@example.com

# Numeric local parts:
123456@example.com

# Long TLDs:
user@example.museum
user@example.photography

# International domains (IDN):
user@münchen.de        # IDN domain
用户@例子.广告          # Fully internationalized (RFC 6532)

# Subdomains:
user@mail.example.com
user@sub.sub.example.co.uk

# Single-char local:
a@example.com

# Plus sign and other special chars in local:
user+a!#$%&'*+/=?^_`{|}~-@example.com

What NOT to validate

Too strict — rejects valid addresses:

// WRONG: rejects most valid emails
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/

// Problems:
// - {2,4} rejects .museum, .photography, .international
// - Doesn't allow new TLDs like .ai, .io
// - Rejects many valid special characters in local part

Treating gmail-style dots as significant: In Gmail, dots in the local part are ignored. alice@gmail.com and a.l.i.c.e@gmail.com deliver to the same inbox. Don’t use this to deduplicate — it’s Gmail-specific behavior.

Server-side validation is mandatory

Never rely only on client-side validation. JavaScript can be disabled or bypassed. Always validate on the server:

// Express.js backend validation:
const { body, validationResult } = require('express-validator');

app.post('/register', [
  body('email')
    .isEmail()
    .normalizeEmail()  // Lowercases, removes dots in gmail, etc.
    .withMessage('Invalid email address'),
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  // Proceed with registration
});

Related posts

Related tool

Email & URL Extractor

Extract every email address and URL from a block of text. Regex-based, case-insensitive, deduplicated, sorted output.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.