Why regex isn't email validation
Checking that an address looks right and checking that it can receive mail are different problems. Most signup forms only solve the first one.
Every signup form validates email addresses. Almost none of them validate the thing that actually matters.
The address oops@gmail.con passes the RFC 5322 regex you copied from Stack Overflow. It passes the HTML5 type="email" check. It passes whatever validation library your framework ships with. It is, by every syntactic measure, a valid email address. It is also a dead end. The user who typed it will sit on your "check your inbox" screen waiting for a message that cannot arrive, and your email provider will record a hard bounce against your sending reputation for the trouble.
Syntax validation answered a question nobody asked.
What you're actually trying to find out
"Is this email valid" collapses four separate questions into one, and they have very different answers, costs, and failure modes.
- Is it well-formed? Does the string parse as an address at all.
- Can the domain receive mail? Does anything on the internet accept mail for the part after the
@. - Does the mailbox exist? Is there a real inbox at that specific address.
- Should you accept it? Is it disposable, is it a shared role address, is it the person's actual address rather than one they typed to get past you.

Each question is harder and less reliable than the one before it. Understanding where the cliff is tells you where to stop.
Layer one: Syntax, and why it barely helps
The full grammar for an email address is genuinely strange. Quoted local parts are legal, so "very odd"@example.com is a real address. Comments are legal, so user(this is a comment)@example.com parses. The local part can be up to 64 octets and is case-sensitive by specification, though almost no provider treats it that way in practice. Internationalized addresses with non-ASCII characters are legal under SMTPUTF8 and increasingly common.
The result is that strict regexes reject real addresses and permissive regexes accept garbage. The famous "correct" RFC 5322 pattern runs to several thousand characters and still gets edge cases wrong.
More importantly, syntax tells you nothing about deliverability. Every typo that matters is syntactically perfect. gmial.com, gmail.con, hotmial.com, yaho.com all parse cleanly. The class of error that costs you users and reputation is exactly the class that syntax checking cannot see.
The practical advice: use a loose check for an @ with something plausible on either side, and stop. Anything stricter buys you false rejections, not accuracy.
Layer two: Can the domain receive mail
This is the layer almost nobody implements and the one that pays for itself.
Look up the domain's MX records. If a domain has no MX record, RFC 5321 says senders fall back to its A or AAAA record, so the absence of MX alone is not proof of anything. If the domain resolves to nothing at all, no mail server on earth will accept mail for it. And if it publishes a null MX record (MX 0 . under RFC 7505), the domain owner is explicitly declaring that it does not accept mail.
Those three signals catch the overwhelming majority of real-world damage. Domain typos are the dominant failure mode in signup forms, and unregistered typo domains fail this check instantly.
It costs one DNS lookup. Results cache well, because the population of domains your users type is heavily concentrated in a handful of consumer providers. The false-rejection rate is close to zero, because a domain with no mail server genuinely cannot receive your verification email no matter how much the user insists it is correct.
The honest caveat: this is not airtight. Typosquatters register near-miss domains precisely because people mistype them, and a registered typo domain will have working mail servers. user@gmial.com may well pass an MX check. Layer two catches the common case, not every case.
Layer three: Does the mailbox exist, and why you should not check
The tempting next step is to open an SMTP connection to the domain's mail server, issue RCPT TO, and see whether it accepts the address. This is where teams get themselves into trouble.
It does not work reliably:
- Catch-all domains accept every recipient at the SMTP layer and sort out delivery later, so acceptance proves nothing. A large share of corporate domains are configured this way.
- Greylisting returns a temporary 4xx failure to unfamiliar senders on purpose, expecting a legitimate sender to retry later. Your synchronous signup form has no "later."
- Major providers frequently accept at
RCPT TOand bounce afterward, deliberately, to avoid confirming which addresses exist.
And it actively harms you. Connecting to mail servers to probe addresses without sending mail is the signature behavior of a directory harvest attack. Mail providers respond by rate limiting you, tarpitting your connections, and eventually adding your sending IPs to blocklists. You can damage the exact reputation you were trying to protect.
There is only one reliable way to prove a mailbox exists and belongs to the person in front of you, and it is the one you were already going to do: send a verification email and require them to act on it.
Layer four: Should you accept it
Disposable domains, role addresses like support@ and info@, and plus-addressed aliases are not correctness problems. They are policy problems, and they should be treated as separate decisions with separate reasoning.
Disposable-domain blocklists go stale immediately. New throwaway domains appear faster than lists update, and lists routinely include domains that turned into legitimate providers. You will block real users and miss most of the fake ones.
Role addresses are frequently the correct address. The person setting up your app for their company may genuinely want billing@ on the account.
If your concern is fraudulent or automated signups rather than mistyped ones, deliverability checking is the wrong tool for it. That is a fraud problem, and it needs signals about behavior, not about the address string.
The constraint that decides everything: Asymmetry
Rejecting a real user is far more expensive than accepting a bad address.
An accepted typo costs you one bounce and one confused user who will probably try again. A wrongly rejected address costs you a signup permanently, and you will never see it in your metrics, because people who get bounced off a form do not file a ticket about it. They leave.
This asymmetry is the whole argument for stopping at layer two. Layers one and four generate false rejections in exchange for marginal accuracy. Layer three generates unreliable answers in exchange for real reputation risk. Layer two rejects only addresses that provably cannot receive mail, which is the one category where rejecting is unambiguously correct.
The second constraint is that this all happens inside a signup form, with a user waiting. A DNS lookup fits comfortably in that budget. A sequence of SMTP conversations across multiple mail servers does not.
Why the signup form is the right place to fix bounce rate
Email reputation is cumulative and slow to repair. Providers track your bounce rate over time, and a sustained rate above roughly two percent gets you throttled or filtered by the major providers. Your password resets and magic links then stop reaching people who typed their address correctly.
The usual response to a reputation problem is downstream: clean your lists, monitor suppressions, warm up a new sending domain. All of that is repair work. The bounce that never happens costs nothing, generates no suppression entry, and needs no cleanup. Catching a typo at the point of entry is the cheapest intervention available, and it is also the only one that helps the user, who finds out about their mistake while they still remember making it.
What we shipped
AuthKit now checks that an address's domain can actually receive mail before accepting a sign-up or an invitation. Addresses whose domains cannot receive mail are rejected at the point of entry, so the user sees their typo immediately instead of waiting for a message that will never arrive.
That is layer two, and deliberately not more. It is a domain-level check, not an SMTP probe, and not a disposable-domain filter. It will not catch a registered typosquat domain with working mail servers, and it does not tell you whether the mailbox exists. Verification email remains the only proof of that, and it still runs exactly as before.
One practical detail if you write tests against sign-up flows: WorkOS reserves the RFC 2606 example domains (example.com, example.net, example.org, and their subdomains) and the RFC 6761 .test top-level domain. Mail to those addresses is accepted by the API and dropped before it reaches the email provider, and they pass the deliverability check. Use them for test users in CI and end-to-end suites rather than inventing a fake domain, which will now correctly fail.
The short version
Use a loose syntax check, verify the domain can receive mail, and send a verification email. Do not probe mailboxes, and treat disposable-address policy as a separate decision from correctness. Most of the value is in one DNS lookup that most signup forms never make.
Learn more about testing with example domains, or read the email deliverability troubleshooting guide if bounces are already affecting your sending.