Skip to content
Start with 100 free verification credits
Qualisend
All articles
Engineering / May 27, 2026

Why regex email validation fails

9 minutes read

Qualisend team
Why regex email validation fails

Every codebase has one: an email validation regex, copied from Stack Overflow, that "validates" addresses. It feels like a check, but it answers a question almost nobody is actually asking. A regex can tell you a string is shaped like an email address. It cannot tell you the address exists, accepts mail, or won't hard-bounce your next campaign — and those are the only things that matter.

The short answer#

Regex validates syntax, not deliverability. definitely-fake@gmail.com is a perfectly valid email address by every regex ever written, and it will bounce the instant you send to it. Worse, the regexes people reach for either reject real addresses (too strict) or accept nonsense (too loose), and the one that is technically correct runs to hundreds or thousands of characters and still can't confirm a mailbox. Use a simple syntax check to catch fat-finger typos at the input, then verify deliverability with a DNS and SMTP check — which is a network problem, not a pattern-matching one.

The regex you've seen, and what it lets through#

Here is the archetype — some variant of this lives in most projects:

const re = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
re.test("definitely-fake@gmail.com"); // true  ← will hard-bounce
re.test("typo@gmial.com");            // true  ← typo'd domain, bounces
re.test("info@company-that-folded.com"); // true  ← dead domain

All three pass. All three are undeliverable. The regex did its job perfectly and told you nothing useful, because syntax and deliverability are different questions. Every address a regex can see is a string; whether a human reads mail at it is a fact about the internet, not about the string.

No pattern can catch a typo'd domain like gmial.com; that takes a comparison against known providers. See the most common email typos, or try the free email typo checker.

The addresses strict regexes wrongly reject#

Strict patterns fail the other way. This typical "tighter" one rejects every valid address in the table below:

const STRICT = /^[\w.-]+@\w+\.[a-z]{2,4}$/i;
AddressFalse assumptionReality
jane+news@example.comLocal parts are only letters, digits, dots, hyphens and underscoresPlus addressing is legal; Gmail and many others deliver +tag mail to the base mailbox
o'brien@example.comNames have no punctuationThe apostrophe is a legal local-part character
jane@studio.photographyTLDs are 2 to 4 lettersLong TLDs like .photography have been live for years
jane@mail.example.co.ukA domain is one name plus a TLD.co.uk and mail subdomains add labels
jane@my-company.comDomains are letters and digits onlyHyphens are legal inside a label

Each rejection is a real person turned away at signup, and none of them shows up in your bounce logs, which is why over-strict patterns survive for years. The syntax check below accepts all five.

The RFC 5322 rabbit hole#

"Fine," the reasoning goes, "I'll use a correct regex." The formal grammar for an email address is defined by RFC 5322, and a regex that actually implements it is famously monstrous: hundreds to thousands of characters, and it permits things you would never want to accept, like quoted local parts with spaces ("a b"@example.com) and comments inside the address.

Two problems follow. First, the strict regex accepts more than you want, not less — it is validating against a 2008 spec, not against "addresses real mail servers will deliver to." Second, even a flawless RFC 5322 match still cannot tell you the domain has a mail server or the mailbox exists. You have spent enormous effort to answer the easy question with more precision, while the hard question — will mail arrive? — remains completely untouched.

Length limits: 64, 255 and 254#

RFC 5321, the SMTP standard, caps the local part (before the @) at 64 octets, the domain at 255, and the whole path at 256, including the angle brackets around the address (<jane@example.com>). That leaves a real ceiling of 254 characters for the address itself.

Some of our language guides check length <= 320 (64 + 1 + 255) instead. It's loose but harmless: everything it rejects is genuinely too long, and anything over 254 that it lets through still has to pass the DNS and SMTP checks.

The three things regex fundamentally cannot do#

No pattern, however clever, can reach across the network. Regex cannot:

  • Check the domain can receive mail. user@company-that-folded.com is valid syntax on a domain with no MX records. Only a DNS lookup reveals the dead domain.
  • Confirm the mailbox exists. noreply-9f2x@gmail.com is valid syntax for an account that was never created. Only an SMTP conversation with Gmail can tell you it isn't there.
  • Detect a catch-all domain. A catch-all server accepts every address, so even a live SMTP check can't confirm a specific mailbox — and a regex has no idea the domain behaves that way.

These aren't gaps in a particular regex; they are outside what any regex can do, because they are questions about servers, not strings. Answering them is the job of email verification, which asks the network rather than the string.

What to actually do: validate in layers#

Deliverability is a pipeline of increasingly expensive checks, and syntax is only the cheap first stage (the full picture is in how email verification works):

  1. Syntax — a simple check at the input to catch obvious typos. This is the only layer regex belongs in.
  2. Domain and MX — a DNS lookup to confirm the domain can receive mail at all. See the Node.js and Python guides for working code.
  3. SMTP mailbox probe — the delivery conversation that actually confirms the mailbox, plus catch-all detection. This needs a mail server with a clean IP reputation and careful rate-limiting, which is why most teams reach for a verification API rather than build it. Proving the person owns the address is a separate job, for a double opt-in email.

The syntax check worth keeping#

For layer 1, don't hand-roll RFC 5322. The pragmatic move is the HTML5 email input in the browser (which applies the WHATWG living-standard pattern for free) and a short, permissive check on the server:

// Pragmatic: rejects fat-finger errors, accepts the addresses real
// mail servers actually deliver to. Not a deliverability check.
const SYNTAX = /^[^\s@."]+(?:\.[^\s@."]+)*@[^\s@.]+(?:\.[^\s@.]+)+$/;

function isPlausibleEmail(email) {
  return (
    email.length <= 254 &&      // RFC 5321 path limit
    email.indexOf("@") <= 64 && // local part limit
    SYNTAX.test(email)
  );
}

isPlausibleEmail("jane@example.com");             // true
isPlausibleEmail("jane+news@mail.example.co.uk"); // true
isPlausibleEmail("not-an-email");                 // false
isPlausibleEmail("a@@b.com");                     // false
isPlausibleEmail("a..b@x.com");                   // false: consecutive dots
isPlausibleEmail(".a@x.com");                     // false: leading dot
isPlausibleEmail("a.@x.com");                     // false: trailing dot

Use it to give instant feedback at the keyboard — nothing more. Treat a pass as "worth checking properly", never as "valid".

The dot is left out of the local-part class, so it can only sit between characters. That rejects the three dot mistakes, accepts all five addresses from the strict-regex table, and keeps the pattern fast. The browser check is a convenience, not a gate: anyone can post to your endpoint directly, so repeat the check on the server.

ReDoS: when the email regex is the vulnerability#

A badly shaped pattern can also stall your service. JavaScript's and Python's built-in engines backtrack: when a match fails, they retry every other way the pattern could split the input. If a repeated group can match the same characters as the part before it, the number of splits grows exponentially.

Earlier versions of this page recommended that shape, [^\s@"]+(?:\.[^\s@"]+)*, where the dot fits both the class and the group's separator. On a string of repeated a. with no @, each extra dot roughly doubled the running time in our own quick test in Node, and an input under 60 characters blocked the process for seconds. That's ReDoS (CWE-1333). If you copied the old pattern, swap in the one above.

Three fixes:

  • Non-overlapping classes. Keeping the dot out of the classes leaves one way to read any input, so the pattern above runs in linear time.
  • A linear-time engine. Go's regexp guarantees it, and RE2 bindings bring it to Node and Python.
  • A parser instead of a pattern, such as Go's net/mail.ParseAddress. Keep Go patched: releases before 1.24.8 and 1.25.2 parse very long domain literals slowly.

A length cap won't save you: this shape stalls far below 254 characters. To test for the flaw, time a non-matching string like "a.".repeat(25) + "!": a vulnerable pattern takes a visible fraction of a second, and a safe one answers instantly.

Syntax checks by language#

Most stacks ship a syntax check, so you rarely need your own pattern. Each guide covers all three layers; this table is layer 1 only.

StackSyntax check
Node.jsThe regex above
Expressexpress-validator isEmail()
Next.jszod z.string().email()
PythonThe regex above
Djangovalidate_email or EmailField
FlaskWTForms Email()
PHPfilter_var() with FILTER_VALIDATE_EMAIL
Laravelemail:rfc,dns (syntax plus a DNS mail-route check)
Gonet/mail.ParseAddress
JavaJakarta Mail InternetAddress
Spring BootJakarta Bean Validation @Email
C#MailAddress.TryCreate
ASP.NET Core[EmailAddress]
RubyURI::MailTo::EMAIL_REGEXP
Railsformat with URI::MailTo::EMAIL_REGEXP

Built-ins disagree at the edges, such as display names (Jane <jane@example.com>); each guide covers its stack's quirks.

Going the rest of the way#

Once syntax passes, the real verification is a network job. You can build the DNS and SMTP layers yourself — the language guides above show how far you can get — but confirming live mailboxes at scale runs into port 25 being blocked on most hosts, IP reputation, and greylisting, which is where a managed API earns its place. Qualisend's POST /verify runs the whole pipeline and returns a four-way verdict (deliverable | risky | undeliverable | unknown) with a reason code, so your code acts on whether the mailbox exists rather than whether the string has an @ in it.

Frequently asked questions

01Is there a correct regex for email validation?

Not in the sense people want. A regex implementing RFC 5322 exists, but it runs to hundreds or thousands of characters, accepts exotic forms you would never want, and still can't confirm the address is deliverable. For practical use, a short permissive pattern (or the HTML5 type="email" input) to catch typos is the right amount of regex - the rest is a DNS and SMTP problem.

02Why does my regex reject valid email addresses?

Because strict patterns bake in assumptions that real addresses break, such as top-level domains of two to four letters (.photography has eleven) or no plus signs in the local part. Over-strict regex is a common source of lost signups. Lean permissive on syntax and verify deliverability separately.

03What is the maximum length of an email address?

254 characters. RFC 5321 caps the part before the @ at 64 characters and the domain at 255, and it limits the whole SMTP path to 256, including the angle brackets around the address. Take away the two brackets and 254 is left for the address itself. Checks that allow up to 320 (64 + 1 + 255) are looser but harmless.

04Does HTML5 email validation check if the address is real?

No. The browser's type="email" validation is a syntax check - a friendlier, standardized version of the same pattern matching. It catches missing @ signs and obvious malformations at the input, but it never contacts the mail server, so it cannot tell you the domain or mailbox exists.

05What should I use instead of regex to validate emails?

Layer it: a permissive syntax check for instant feedback, a DNS/MX lookup to rule out dead domains, and an SMTP mailbox probe to confirm delivery. Build the first two yourself; use a verification API for the SMTP layer, since doing it reliably needs sending-IP reputation and rate-limiting a regex could never provide.


Want to see the difference between "valid syntax" and "will actually deliver"? The free plan includes 100 verification credits that run the full pipeline — syntax, DNS, and the SMTP mailbox probe — so you can watch a regex-approved address come back undeliverable. Without an account, paste typo@gmial.com into the free email checker: every regex here accepts it, and the checker suggests gmail.com. The API reference has copy-paste examples in seven languages.

Your reputation, protected.

Clean your first list in minutes. 100 free credits, no card required.

Get started