Blog
Email validation regex: the pattern and its limits
The pattern worth using, the valid addresses the popular ones reject, and the one question no regular expression can answer.
VerifyInbox · published 11 September 2026 · 4 min read
If you only want the pattern: use the one the HTML standard defines for input[type=email], reproduced below. It is the most widely implemented email pattern in existence, it is what every browser already applies to an email input, and agreeing with the browser is worth more than being cleverer than it.
The rest of this page is about what that pattern does and does not do, which matters because a validation rule that rejects a real customer’s address is a bug that never gets reported — the customer just leaves.
The pattern
const EMAIL =
/^[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])?)*$/;The specification is explicit that this is a willful violation of RFC 5322: it is deliberately both stricter and more permissive than the standard grammar, because the full grammar is impractical in a form field and because browsers needed one rule they could all implement identically. Knowing that it is a deliberate compromise is most of what you need to use it well.
What the common patterns get wrong
Here is that pattern beside the kind of expression that circulates on forums, run over eight addresses. Every result below is from an actual run under Node 22.
const NAIVE = /^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
const HTML5 =
/^[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])?)*$/;
const cases = [
"[email protected]",
"[email protected]",
"[email protected]",
"o'[email protected]",
"jane@münchen.de",
"[email protected]",
"[email protected]",
"jane@example",
];
console.log("address".padEnd(30), "naive", " html5");
for (const c of cases)
console.log(c.padEnd(30), String(NAIVE.test(c)).padEnd(5), String(HTML5.test(c)));Output
address naive html5
[email protected] true true
[email protected] false true
[email protected] false true
o'[email protected] false true
jane@münchen.de false false
[email protected] true true
[email protected] true true
jane@example false trueRead that table row by row, because almost every row is a decision someone made.
jane+newsletter@— valid, widely used, rejected by the naive pattern because+is missing from its local-part class. This is the single most common false rejection in the wild.[email protected]— valid, rejected by the{2,4}TLD assumption. That assumption was reasonable before the new gTLD programme and has been wrong ever since.o'[email protected]— valid; apostrophes are legal in a local part and real people have them in their names. Rejected by the naive pattern, accepted by the standard one.jane@münchen.de— rejected by both, correctly for the HTML pattern: an internationalised address needs RFC 6530 handling, not a wider character class. Convert the domain to punycode first.[email protected]— the same address after punycode conversion, and now both accept it. This is the practical route: normalise, then validate.[email protected]— accepted by both, and invalid. Consecutive dots are not permitted in an unquoted local part. The HTML pattern lets it through by design; if this matters to you, a parser is the answer, not a longer expression.jane@example— a bare hostname with no dot. Accepted by the HTML pattern deliberately, because intranet addresses are real. Almost certainly wrong on a public signup form, so add a dot requirement there if you want one — as a separate rule, not by editing the pattern.
The failure mode nobody mentions
Several widely copied "RFC-compliant" email patterns contain nested quantifiers over overlapping character classes. On a crafted input those backtrack exponentially, and a validation routine becomes a way to pin a CPU core — from a text field, unauthenticated, which is about the most convenient denial-of-service surface a web application can offer.
The pattern above does not have that shape. If you write your own, check it against a long non-matching input before it goes anywhere near a request handler, and prefer a parser for anything complicated.
Where a regex is the right tool
Narrowly, and usefully: catching a typo in a form field before the user submits it, in the browser, with an error message they can act on. That is a genuine improvement to a signup flow and the HTML pattern does it for free — a plain <input type="email" required> applies it with no JavaScript at all.
Everywhere else, use a parser. In Python that is email-validator; in JavaScript it is validator.js or the email check in your schema library; in most other ecosystems there is one obvious choice. All of them handle the grammar, and all of them give you a normalised address, which a regex never does.
What no pattern can do
A regular expression operates on a string. The question you actually have — will mail to this address arrive — is a question about the state of somebody else’s mail server, and no amount of pattern matching reaches it.
| Layer | Question | Tool |
|---|---|---|
| Syntax | Is this well-formed? | A regex, or better, a parser |
| Domain / MX | Can this domain receive mail? | A DNS lookup |
| Mailbox | Does this mailbox exist? | An SMTP conversation with the receiving server |
[email protected] passes every pattern on this page and passes a DNS check too. It is almost certainly not a mailbox. Only the third layer distinguishes them, and it produces three answers rather than two — deliverable, undeliverable, and unknown for when the server declined to say.
If you have an address that passed your pattern and you want to know whether it is real, run it through the verifier. The Python route, with runnable code, is in validating an email address in Python.
Terms used on this page
Sources
- HTML Standard — valid e-mail address
- RFC 5322 — Internet Message Format
- RFC 6530 — Overview and Framework for Internationalized Email
- RFC 3696 — Application Techniques for Checking and Transformation of Names
Primary sources checked 11 September 2026. If something here is out of date, tell us and we will correct it.
More: How to find someone's email address · How to validate an email address in Python · Gmail, Yahoo and Microsoft sender requirements · How to block disposable email addresses