Regex for Email Validation: What Actually Works
Email validation with regex is one of the most bikeshedded topics in web development. There are regex patterns that are thousands of characters long trying to match the full RFC 5322 spec, and there are simple one-liners that catch 99% of real-world cases. Here's a practical breakdown of what to use and when.
The simple pattern (good enough for most forms)
^[^\s@]+@[^\s@]+\.[^\s@]+$
This checks:
- At least one non-whitespace, non-
@character before the@ - An
@symbol - At least one non-whitespace, non-
@character after@ - A
.somewhere after the@ - At least one character after the final
.
It passes user@example.com, first.last@subdomain.domain.co, and user+tag@gmail.com. It rejects strings with spaces, no @, and no . in the domain.
For a login form or newsletter signup, this is usually enough.
A more complete pattern
^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$
This adds:
- Allowed characters in the local part: alphanumeric,
.,_,%,+,- - Domain must be alphanumeric with hyphens and dots
- TLD must be 2+ alphabetic characters
This pattern rejects most obviously invalid inputs while accepting all common real-world email formats including user@example.co.uk, first+filter@mail.example.org, and 123@numbers.com.
In JavaScript:
function isValidEmail(email) {
return /^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/.test(email);
}
In Python:
import re
EMAIL_RE = re.compile(r'^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$')
def is_valid_email(email: str) -> bool:
return bool(EMAIL_RE.match(email))
What regex cannot tell you
Regex only validates the format of an email address. It cannot tell you:
- Whether the mailbox actually exists
- Whether the domain accepts mail (has MX records)
- Whether the user typed their own address or someone else's
- Whether the address is currently active
A syntactically valid address like president@whitehouse.com will pass any regex — but if someone typed it into your form, they didn't type their own email.
For anything beyond format checking (account creation, notification systems, mailing lists), you need to send a verification email and have the user click a link.
The HTML approach (often overlooked)
<input type="email" name="email" required>
Modern browsers implement their own email validation on type="email" inputs — similar in strictness to the second pattern above. You get format validation for free with no JavaScript.
Always pair this with server-side validation (the same regex) since clients can bypass HTML validation.
When to use a validation library instead
If you're building a system where email quality matters — transactional email, billing, authentication — consider a dedicated library:
- Python:
email-validator(pip install email-validator) — validates format, checks DNS for MX records. - JavaScript/Node:
validator.js(npm install validator) —isEmail()with configurable strictness. - Hosted API: Abstract API, Hunter, or similar services can verify deliverability in real time.
For a basic contact form, these are overkill. For a SaaS signup flow, the extra step is worth it.
Common edge cases to test
| Address | Should pass? |
|---|---|
user@example.com |
Yes |
user+tag@gmail.com |
Yes (plus addressing is valid) |
user@sub.domain.co.uk |
Yes |
user@localhost |
No (no TLD) |
@example.com |
No (empty local part) |
user @example.com |
No (space in local part) |
user@.example.com |
No (dot before domain) |
user@example |
No (no TLD) |
Run your chosen regex against these before shipping.
Key takeaways
- Use
^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$for most form validation. - Regex checks format only — it cannot verify that the address exists or is active.
- Always validate on the server, not just the client.
- For critical flows (auth, billing), send a verification email regardless of what the regex says.
- HTML
type="email"gives you format validation in browsers for free.