The safest email regex for most applications is the one browsers already run against <input type="email"> fields, defined directly in the WHATWG HTML Living Standard:
/^[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])?)*$/
It rejects the malformed input a signup form actually needs to catch: missing @ signs, empty domain labels, stray characters, and it ships in every major browser today. What it cannot do is confirm the mailbox on the other end exists. A regex checks shape, not delivery. Confirming a mailbox actually accepts mail takes a DNS lookup and, for real certainty, an SMTP conversation like the one described in What Is SMTP? This guide gives the pattern in JavaScript and Python, explains why the “fully RFC 5322 compliant” regex people search for is the wrong goal, and lays out where syntax checking should stop and real verification should start.
The Regex Browsers Already Use
This pattern is not a random Stack Overflow regex. The WHATWG spec defines it as an implementation of an ABNF grammar built for the email input state, referencing RFC 5322 for character rules and RFC 1034 for domain label length. Copying it server-side gives an API endpoint the same validation behavior as the browser’s own constraint checking.
JavaScript:
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 isSyntacticallyValid(email) {
return EMAIL_RE.test(email);
}
Python:
import re
EMAIL_RE = re.compile(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+"
r"@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?"
r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
)
def is_syntactically_valid(email: str) -> bool:
return bool(EMAIL_RE.match(email))
The forward slash does not need escaping in Python’s re module, since Python has no /regex/ literal syntax the way JavaScript does; the pattern is otherwise identical.
The domain half of the pattern enforces a real constraint: each label between dots starts and ends with a letter or digit, runs 1 to 63 characters, and uses hyphens only in the middle. That 63-character ceiling is not arbitrary: the WHATWG spec ties it to the DNS label limit in RFC 1034 Section 3.5. The local part before the @ stays deliberately permissive, allowing every character RFC 5322 permits there, including the + that Gmail and other providers use for address tagging ([email protected]).
Quotable passage: The WHATWG email regex is the same pattern every major browser runs against <input type="email">. It enforces DNS label rules on the domain (1 to 63 characters per label, no leading or trailing hyphens) and stays permissive on the local part, accepting any character RFC 5322 allows there without requiring a quoted string.
Why the “Complete” RFC 5322 Regex Is a Trap
Search for “RFC 5322 email regex” and long patterns turn up, built to accept every address the specification technically permits. RFC 5322 Section 3.4.1 defines the grammar as addr-spec = local-part "@" domain, where local-part = dot-atom / quoted-string / obs-local-part. The quoted-string branch is what turns a simple pattern into an unreasonable one: it permits spaces, otherwise-illegal punctuation, and backslash-escaped characters between quotes, so a local part like "very unusual name"@example.com is technically valid RFC 5322. The obs-local-part branch adds obsolete forms kept for compatibility with decades-old mail software almost nothing still generates.
The WHATWG spec is blunt about why it does not attempt any of this. Implementing RFC 5322 in full would be “a willful violation of RFC 5322, which defines a syntax for email addresses that is simultaneously too strict (before the ‘@’ character), too vague (after the ‘@’ character), and too lax (allowing comments, whitespace characters, and quoted strings in manners unfamiliar to most users) to be of practical use here.” That sentence answers “why doesn’t my regex handle every RFC 5322 address”: handling every address means accepting syntax that confuses the person typing it in, syntax essentially no real provider issues.
What the Regex Catches (and What It Misses)
Comparing the WHATWG pattern directly against the RFC 5322 grammar surfaces edge cases most email-regex articles skip over:
| Address | Passes the WHATWG/browser regex | Valid per RFC 5322 | Why |
|---|---|---|---|
[email protected] | Yes | Yes | The ordinary case both agree on |
[email protected] | Yes | Yes | + is a permitted local-part character |
[email protected] | Yes | Yes (syntax only) | Neither grammar checks whether the domain resolves or has mail servers |
jane@localhost | Yes | Yes (syntax only) | No dot or top-level domain is required by either grammar |
[email protected] | Yes | No | RFC 5322’s dot-atom disallows unquoted leading, trailing, or consecutive dots; the regex’s character class does not enforce this |
"jane doe"@example.com | No | Yes | Quoted-string local parts are valid RFC 5322 but fall outside the regex’s character set |
jane@[192.0.2.1] | No | Yes | RFC 5322’s domain-literal (a bracketed IP address) has no equivalent in the WHATWG grammar |
Two takeaways stand out: a passing regex says nothing about whether the domain exists, and the pattern is looser than RFC 5322 in one direction (consecutive dots) while stricter in another (no quoted strings, no bracketed IP domains).
Syntax Isn’t Deliverability
None of the checks so far say anything about the mailbox itself. A regex, however carefully built, only confirms the string is shaped like an address; it cannot ask example.com whether jane is a real mailbox, a deleted account, or a typo for jame. That answer comes only from the receiving mail server, during the SMTP conversation described in What Is SMTP? RFC 5321 Section 4.1.1.3 spells out what happens when the answer is no: “If the recipient is known not to be a deliverable address, the SMTP server returns a 550 reply, typically with a string such as ‘no such user -‘ and the mailbox name.” Getting that 550 requires an actual RCPT TO command against a live mail server, something nothing running inside an application’s memory can produce.
MDN’s own documentation on the <input type="email"> element draws the same line. Browser-side validation “ensures that the value of the field is properly formatted to be an email address,” but is explicitly “not enough to ensure that the specified text is an email address which actually exists, corresponds to the user of the site, or is acceptable in any other way.”
Even an SMTP-level check has a blind spot: catch-all email addresses. A domain configured to accept mail for any local part returns 250 OK for [email protected] exactly as readily as for a real employee’s address. Regex cannot detect this, and neither can a bare RCPT probe; only what happens after a real send, a bounce or the lack of one, reveals the difference.
Quotable passage: A syntax check and a deliverability check answer different questions. Regex confirms a string is shaped like an email address. Only a DNS lookup for mail servers, followed by an SMTP conversation with the receiving server, confirms a mailbox exists and is willing to accept a message right now.
A Validation Pipeline That Actually Works
For most signup forms and API integrations, three layers cover the practical range, roughly in order of cost:
- Syntax check. The regex above, or a language’s built-in email type, rejects obviously malformed input for close to zero computational cost. It catches typos: a missing @, a stray space, an empty domain.
- MX record lookup. A quick DNS query confirms the domain has a mail server willing to accept messages at all. A domain with no MX record and no usable fallback A record cannot receive mail, and this check costs a few milliseconds without ever sending a message.
- Real confirmation. The only way to know a mailbox is monitored by an actual person is to hear back from it: a clicked confirmation link, a completed double opt-in flow, or a reply to a transactional message. Sending platforms report what happens after that send through bounce and complaint events, the closest a syntax check ever gets to ground truth. Coldletter surfaces those events through its API, so a bad address shows up as a bounce signal inside the same integration that sent the message.
Skipping straight from layer one to production sending is how a clean-looking signup list quietly builds up an email bounce rate that damages sender reputation months later. Layer two is cheap insurance. Layer three is the only layer that actually confirms someone wanted the mail in the first place.
Frequently Asked Questions
What is the best regex for validating an email address?
The WHATWG HTML Living Standard’s pattern, the same one browsers use for <input type="email">, is the most practical choice. It appears in full earlier in this guide alongside JavaScript and Python implementations. It catches obviously malformed input without rejecting address formats that real signup forms actually see in practice.
Does an email regex guarantee the address is deliverable?
No. A regex only confirms the string is shaped like an email address. Confirming the mailbox actually exists and accepts mail requires a DNS lookup for mail servers and, for full certainty, a live SMTP conversation with the receiving server.
Why shouldn’t I use a regex that fully implements RFC 5322?
RFC 5322 permits quoted-string local parts with spaces and escaped punctuation, plus obsolete syntax forms from decades-old mail software. A regex that accepts all of this also accepts address formats that confuse users, formats that essentially no real provider issues, without rejecting anything a simpler pattern would miss.
Does a valid email address need a top-level domain?
Not according to the WHATWG regex or the RFC 5322 grammar it draws from. Addresses like user@localhost pass both, since neither requires a dot in the domain. In practice almost every real-world address does have a top-level domain, so many applications add that as an extra requirement on top of the base pattern.
How do I check if an email domain can actually receive mail?
Query the domain’s MX records. If none exist, check for a usable A record as a fallback, per standard mail routing behavior. A domain with neither cannot accept mail regardless of what the local part looks like. This check runs over DNS in milliseconds and does not require sending a message.
What’s the difference between email validation and email verification?
Validation checks the string’s format, typically with a regex. Verification checks whether the address is actually real and reachable, usually through an MX lookup plus an SMTP conversation with the receiving server, or through a real send that produces a bounce or a confirmed click. Validation is instant and free; verification takes longer and is the only step that confirms deliverability.
I’ve spent my career building software at scale with a soft spot for email: deliverability, lifecycle campaigns, and getting messages to actually land. I started Coldletter to fix what bugged me about transactional and marketing email tools. I’m based in Vancouver.
