Email regex vs MX check: what each one actually catches
A regex tells you whether a string looks like an email address. An MX lookup tells you whether the domain after the @ is set up to receive mail. They answer different questions, and most signup forms only ask the first one.
What a regex can and cannot tell you
| Input | Passes a typical regex | Passes an MX check |
|---|---|---|
[email protected] | Yes | Yes |
[email protected] (typo) | Yes | Often yes, because typo domains can have MX records |
[email protected] | Yes | No (no MX, no A record) |
[email protected] (disposable) | Yes | Yes, it receives mail perfectly well |
jane@ | No | Not applicable |
Two rows stand out. A typo domain and a disposable domain both pass an MX check, because both are real mail servers. That is why a domain list (disposable domains) and a typo dictionary (common misspellings of gmail, hotmail, yahoo, outlook, icloud) are separate layers on top of MX.
A regex that does not reject real people
const looksLikeEmail = /^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/.test(email);
Keep it this loose. Plus tags ([email protected]), new long top-level domains and subdomains are all valid. Stricter patterns copied from forums regularly reject them.
An MX check in Node.js
import { resolveMx, resolve4 } from "node:dns/promises";
async function domainCanReceiveMail(email) {
const domain = email.split("@")[1];
try {
const mx = await resolveMx(domain);
if (mx.length) return "mx";
} catch {}
try {
const a = await resolve4(domain); // RFC 5321 falls back to the A record
if (a.length) return "a-only";
} catch {}
return "none";
}
A domain with MX records is the strong signal. A domain with only an A record can technically receive mail but usually does not, so treat a-only as risky rather than valid. Browsers cannot run DNS queries, so this runs on your server.
Or get all the layers in one request
The MailGate endpoint does syntax, MX, disposable-domain, role-account and typo checks and returns a 0 to 100 score with a verdict:
curl "https://pw-d20261007a2.plainwork-apps.workers.dev/[email protected]"
# {"valid_syntax":true,"disposable":false,"did_you_mean":"[email protected]","mx":"a-only","score":45,"verdict":"risky", ...}
What none of these can do
No regex or domain-level check can prove a specific mailbox exists or belongs to the person typing it. Only a confirmation link does that. Use the cheap checks to avoid sending confirmation emails that will bounce, and the confirmation email as the final gate.
Related: validating email in JavaScript, blocking disposable signups, and the free list cleaner (runs in your browser). The free API tier is 20 checks a day with no signup; the $5/month plan removes the cap.