Email validation regex tester
Five email regexes people copy into real forms, run against addresses real people have and typos real people make. The matrix shows where each pattern turns away a valid address and where it lets a broken one through. Add your own regex and addresses, then copy the pattern you settle on for JavaScript, Python or PHP. Everything runs in your browser.
HTML spec (input type=email)
WHATWG HTML Standard, valid email address
/^[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])?)*$/What browsers check for <input type="email">. The spec calls it "a willful violation" of RFC 5322: looser before the @, stricter about the domain, and no quoted strings.
Every pattern against every address
- Rejects a real address
- Lets a bad one through
- As expected
| Address and expected verdict | |||||
|---|---|---|---|---|---|
jane.doe@example.comAcceptRFC validA plain address. A pattern that fails this is broken. | pass, as expected | pass, as expected | pass, as expected | pass, as expected | pass, as expected |
jane+invoices@example.comAcceptRFC validPlus addressing (RFC 5233). People use it to sort their mail, and rejecting it turns away careful users. | pass, as expected | pass, as expected | pass, as expected | pass, as expected | pass, as expected |
jane@mail.example.co.ukAcceptRFC validSubdomains and a two-part country suffix. | pass, as expected | pass, as expected | pass, as expected | pass, as expected | pass, as expected |
jane@studio.photographyAcceptRFC validA long newer top-level domain. Patterns that cap the TLD at four letters fail here. | pass, as expected | pass, as expected | pass, as expected | pass, as expected | pass, as expected |
jane@example.xn--p1aiAcceptRFC validThe .рф domain in its ASCII form. Top-level domains can contain digits and hyphens. | pass, as expected | pass, as expected | fail, rejects a real address | fail, rejects a real address | pass, as expected |
o'brien@example.ieAcceptRFC validAn apostrophe is a permitted character, and a common one in Irish surnames. | pass, as expected | pass, as expected | fail, rejects a real address | pass, as expected | pass, as expected |
mary-jane@my-studio.comAcceptRFC validHyphens on both sides of the @. | pass, as expected | pass, as expected | pass, as expected | pass, as expected | pass, as expected |
12345@example.comAcceptRFC validA local part made only of digits is fine. | pass, as expected | pass, as expected | pass, as expected | pass, as expected | pass, as expected |
Jane.Doe@Example.COMAcceptRFC validCapitals. Domains ignore case, so a pattern should too. | pass, as expected | pass, as expected | pass, as expected | pass, as expected | pass, as expected |
jane@bücher.deAcceptValid as punycodeAn internationalised domain. The spec's regex is ASCII-only, but browsers may convert the domain to punycode (xn--bcher-kva.de) before checking. | fail, rejects a real address | pass, as expected | fail, rejects a real address | fail, rejects a real address | fail, rejects a real address |
"jane doe"@example.comYour callRFC validA quoted local part may contain a space under RFC 5321 and 5322. The HTML spec rejects quoting on purpose, and real addresses like this are rare. | fail, no expected verdict | fail, no expected verdict | fail, no expected verdict | fail, no expected verdict | fail, no expected verdict |
jane@[192.0.2.1]Your callRFC validAn IP address literal, valid under RFC 5321. The HTML spec rejects it, and it is almost never what a person means to type. | fail, no expected verdict | pass, no expected verdict | fail, no expected verdict | pass, no expected verdict | pass, no expected verdict |
josé@example.comYour callSMTPUTF8 onlyA non-ASCII local part works only where both mail systems support SMTPUTF8 (RFC 6531). The HTML spec rejects it. | fail, no expected verdict | pass, no expected verdict | fail, no expected verdict | pass, no expected verdict | fail, no expected verdict |
jane..doe@example.comRejectRFC invalidTwo dots in a row are not allowed unless the local part is quoted. The HTML spec accepts it anyway. | pass, lets a bad address through | pass, lets a bad address through | pass, lets a bad address through | fail, as expected | fail, as expected |
.jane@example.comRejectRFC invalidA local part cannot start with a dot. | pass, lets a bad address through | pass, lets a bad address through | pass, lets a bad address through | fail, as expected | fail, as expected |
jane.@example.comRejectRFC invalidNor end with one. | pass, lets a bad address through | pass, lets a bad address through | pass, lets a bad address through | fail, as expected | fail, as expected |
jane@exampleRejectValid syntax onlyValid syntax, and the HTML spec accepts it, which suits intranet hosts. Internet mail needs a fully qualified domain (RFC 5321), so on a public form it is a typo. | pass, lets a bad address through | fail, as expected | fail, as expected | fail, as expected | fail, as expected |
jane doe@example.comRejectRFC invalidAn unquoted space is never valid. | fail, as expected | fail, as expected | fail, as expected | fail, as expected | fail, as expected |
jane@example.com.RejectRFC invalidA trailing dot is DNS notation, not part of an email address. | fail, as expected | pass, lets a bad address through | fail, as expected | fail, as expected | fail, as expected |
jane@-example.comRejectRFC invalidA domain label cannot start with a hyphen. | fail, as expected | pass, lets a bad address through | pass, lets a bad address through | pass, lets a bad address through | fail, as expected |
jane@example..comRejectRFC invalidTwo dots leave an empty label in the domain. | fail, as expected | pass, lets a bad address through | pass, lets a bad address through | fail, as expected | fail, as expected |
jane.example.comRejectRFC invalidNo @ at all. | fail, as expected | fail, as expected | fail, as expected | fail, as expected | fail, as expected |
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@example.comRejectRFC invalid65 characters before the @. RFC 5321 allows 64, and none of these patterns count. | pass, lets a bad address through | pass, lets a bad address through | pass, lets a bad address through | pass, lets a bad address through | pass, lets a bad address through |
| Mistakes on the built-in set | 1 lost 5 let in | 0 lost 7 let in | 3 lost 6 let in | 2 lost 2 let in | 1 lost 1 let in |
One per line, up to 50. They join the matrix with no expected verdict, so the call is yours.
Copy it: HTML spec (input type=email)
Pick a column or tab above to change the pattern. The same regex gives the same answers in all three languages.
// HTML spec (input type=email)
const EMAIL_RE = /^[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])?)*$/;
function looksLikeEmail(value) {
return EMAIL_RE.test(value.trim());
}What a regex can tell you, and what it cannot
A regex checks shape. It can confirm one @, a local part made of permitted characters and a domain that looks like a hostname. It cannot tell you the mailbox exists. jane@exampel.com passes every pattern on this page, and so does an address that was closed last year. The only proof that an address works is a message that arrives, which is why sign-ups that matter send a confirmation link.
Length is the other blind spot. RFC 5321 caps the part before the @ at 64 octets and the whole path at 256 including its angle brackets, which leaves 254 for the address. None of these patterns count, so put maxlength="254" on the field and check the local part on the server if it matters to you.
Why the HTML spec pattern is a sensible default
The pattern the HTML standard defines for <input type="email"> is the one the browser already checks, so using it on the server as well means the two never disagree. The standard calls it "a willful violation of RFC 5322", which it describes as too strict before the @, too vague after it, and too lax about comments, whitespace and quoted strings to be of practical use in a form.
In the matrix its mistakes are nearly all on the forgiving side. It accepts dots RFC 5322 forbids, as in jane..doe@, and a domain with no dot at all, which suits intranets. It refuses quoted local parts and IP literals, which almost nobody types. The one real address its bare regex refuses is an internationalised domain such as bücher.de, and the standard lets browsers convert that to punycode before checking. If you need stricter rules than this, add them as separate checks with their own error messages rather than one longer regex.
Two alternatives hold up well in the matrix. The RFC 5322 pattern makes fewer mistakes on this set, but it runs to nearly 500 characters that nobody on your team will review, and it accepts quoted and IP-literal addresses that your mail provider or CRM may refuse. The minimal pattern rejects no real address at all and leaves the rest to a confirmation email, which is a fair choice when losing a sign-up costs more than a bounce.
Over-strict patterns lose real customers
A pattern that lets a typo through costs you a bounced email. A pattern that rejects a real address costs you the person, and you never find out, because the form simply refuses to submit. That asymmetry is the whole case for erring loose.
The victims are predictable. Apostrophes, as in o'brien@, are permitted characters that the classic [a-zA-Z0-9._%+-] class leaves out. Plus addressing, as in jane+invoices@, belongs to the people most careful about their inbox. A pattern ending in [a-zA-Z]{2,4} rejects .photography and every other long top-level domain, and one that insists on letters rejects internationalised domains in their ASCII form, like xn--p1ai. Top-level domains are added over time, so a regex is the wrong place to keep a list of them.
Validate again on the server
Browser validation is a convenience for the person typing. Anything can post to your form's endpoint without running it: a script, a bot, a form with novalidate, or form.submit() called from JavaScript, which skips validation altogether. So check again where the data lands: trim whitespace, cap the length at 254, test the same pattern, and lowercase only the domain, since RFC 5321 lets the part before the @ be case-sensitive. Then, if the address matters, send a confirmation.
If your form posts to WebForms, an email field is stored exactly as it was submitted. WebForms does not refuse a submission because an address looks unusual, so the check in your form is the one that applies. The spam filter does look at the domain: known disposable providers can add to the spam score or be blocked outright, and the disposable email checker runs that kind of check in your browser.
The _replyto field is stricter, because its value becomes the Reply-To header on your notification. It has to be a single valid address, and if it is not, it is ignored: the notification goes out without a Reply-To rather than the submission failing. On paid plans the autoresponder writes back to _replyto when it is present, otherwise to a field named email, or failing that the first value that looks like an address.
To show errors inline rather than in the browser's bubble, start from the JavaScript validation form. For phone numbers, postcodes and other fields, build the attribute with the input pattern generator.