C.W.K.
Stream
Lesson 01 of 12 · published

Email Validation — The Sane Default

~10 min · email, validation

Level 0Pattern-Curious
0 XP0/90 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete

The 99% pattern

Real email regex (RFC 5322 compliant) is over 6,000 characters and includes nested comments, quoted strings, IP literals, and Unicode. Don't try to write it. The sane production default is:

^[\w.+-]+@[\w-]+\.[\w.-]+$

Reading left to right:

  • [\w.+-]+ — local part: word characters plus . + -
  • @ — literal at-sign
  • [\w-]+ — domain label (no dots inside)
  • \. — literal dot
  • [\w.-]+ — TLD with allowed subdomains and hyphens

What this catches

Typos: missing @, missing TLD, spaces in addresses, completely invalid shapes. About 95% of "is this an email?" use cases.

What it MISSES (and why that's fine)

  • Quoted local parts: "hello world"@example.com — valid per spec, almost never used.
  • IP-literal domains: user@[192.168.1.1] — same.
  • Non-ASCII Unicode local parts: 유저@한국.kr — needs Unicode flag and adjusted character classes.
  • Whether the email actually exists.

For real "does this email work?" — send a verification message. Regex can't tell you that.

The HTML5 alternative

Browsers' built-in type="email" validation uses a slightly more permissive regex. If you're in a browser, just use it: <input type="email" required>. Saves you the regex entirely.

Code

Email validation·python
import re

EMAIL = re.compile(r'^[\w.+-]+@[\w-]+\.[\w.-]+$')

# Tests
bool(EMAIL.fullmatch('hi@example.com'))         # True
bool(EMAIL.fullmatch('hi+tag@example.co.kr'))   # True
bool(EMAIL.fullmatch('user.name@sub.domain.org'))  # True

# Catches
bool(EMAIL.fullmatch('no-at-sign'))             # False
bool(EMAIL.fullmatch('@no-local'))              # False
bool(EMAIL.fullmatch('hi@example'))             # False (no TLD)
bool(EMAIL.fullmatch('hi @example.com'))        # False (space)

# Find emails in text (no anchoring)
EMAIL_FIND = re.compile(r'[\w.+-]+@[\w-]+\.[\w.-]+')
re.findall(EMAIL_FIND, 'reach me at hi@pippa.dev or admin@cwk.io')
# ['hi@pippa.dev', 'admin@cwk.io']

External links

Exercise

Take an email validator from your codebase. Compare to the 99% pattern above. Is yours longer? Does the extra complexity actually catch anything real, or is it noise?

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.