Regex for email validation: the pattern that actually works
Searching for an email regex returns two kinds of answers: patterns so loose they accept a@b, and a 6,000 character monster that claims full RFC 5322 compliance. Neither is what you want. Here is the pattern that experienced teams actually ship, why it looks the way it does, and where regex validation genuinely stops helping.
The pattern to use
^[^\s@]+@[^\s@]+\.[^\s@]+$
Read it piece by piece:
^and$anchor the match so the whole input must be an email, not merely contain one.[^\s@]+before the@accepts one or more characters that are not whitespace and not@. This deliberately allows dots, plus signs, hyphens, and unicode, because real local parts contain all of them.@matches the single required at sign.[^\s@]+\.[^\s@]+after it requires a domain with at least one dot, souser@localhostfails butuser@example.compasses.
This is the same shape HTML5 uses for <input type="email">, and it embodies a philosophy: the job of an email regex is to catch typos and obvious garbage, not to referee the RFC.
Why not the "fully correct" RFC pattern
RFC 5322 permits addresses most people would call invalid, including quoted local parts with spaces ("john smith"@example.com), comments in parentheses, and address literals like user@[192.168.1.1]. A regex that honors all of that runs to thousands of characters, is impossible to review, and still cannot tell you the one thing you care about: whether the mailbox exists.
Meanwhile the strict pattern rejects real addresses. Older popular patterns like ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$ fail on .museum and .technology domains (the {2,4} TLD limit stopped being true in 2013) and on internationalized addresses. Every character class you tighten is a bet that no legitimate user will ever fall outside it, and that bet keeps losing as email evolves.
The loose pattern never rejects a deliverable address. The strict one sometimes does. Since neither can confirm deliverability, the loose one wins.
The same pattern across languages
JavaScript
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
emailRegex.test('user@example.com') // true
Python
import re
email_regex = re.compile(r'^[^\s@]+@[^\s@]+\.[^\s@]+$')
bool(email_regex.match('user@example.com')) # True
HTML, which needs no regex at all:
<input type="email" required>
The browser applies its own built-in pattern and blocks submission on failure. Add your regex server side anyway, since anyone can bypass client validation.
Test before you trust
Whatever pattern you choose, run it against a hostile list before shipping. A reasonable set:
| Input | Should pass |
|---|---|
user@example.com | yes |
first.last+tag@sub.example.co.uk | yes |
user@example.museum | yes |
plainaddress | no |
user@localhost | no |
user @example.com | no |
user@@example.com | no |
user@.com | depends on how strict you want the domain check |
Paste the pattern and this list into the Regex Tester and you will see every match highlighted live, which makes it obvious the moment a tweak starts rejecting valid input.
What regex cannot tell you
A syntactically valid address can still be undeliverable, abandoned, or fake. If correctness matters, layer the checks:
- Regex catches typos at the form level, instantly and offline.
- MX lookup confirms the domain can receive mail at all. A domain with no MX records will bounce everything; you can check any domain with the MX Lookup tool.
- Confirmation email is the only real proof. Send a link; if it gets clicked, the address exists and the user controls it.
Most signup flows need step 1 and step 3. Trying to make regex do the whole job is how the 6,000 character pattern got written.
Common regex mistakes in email patterns
- Forgetting the anchors. Without
^...$, the pattern matchesnot an email user@example.com eitherbecause a valid address appears somewhere inside. - Unescaped dots. A bare
.matches any character, so@example.comalso matches@exampleXcom. Escape it as\.when you mean a literal dot. - Case sensitivity worries. Domains are case insensitive and in practice local parts are too. Lowercase the input before storing rather than complicating the pattern.
- Catastrophic backtracking. Nested quantifiers like
([a-z]+)*@can hang on crafted input. The recommended pattern has no nesting, so it runs in linear time.
If you want to go deeper on advanced pattern techniques, regex lookahead and lookbehind covers the zero-width assertions that more complex validations lean on.