Why an email validation regex is not enough

published · September 3, 2026

A regex only rejects the impossible. It cannot say whether the domain accepts mail, the mailbox exists, or the address is a burner. What to run after it.

A regular expression can reject strings that cannot be email addresses; it cannot accept strings that are. [email protected], [email protected] and [email protected] all pass every regex ever written, and each one is a problem for a different reason that only DNS, a list, or the mail server itself can reveal. Use a short pattern to catch typing errors instantly, then verify the address for real.

What does the RFC actually allow?

RFC 5322 defines the address grammar, and it is far wider than most forms expect. The local part may be a quoted string containing spaces and @ signs. Comments in parentheses are legal in several places. The domain may be an IP address in square brackets. The regex that implements the full grammar is thousands of characters long, and matching it still tells you nothing useful, because "syntactically valid" and "a mailbox someone reads" are different questions.

The HTML specification makes the same point from the other side. The WHATWG definition of a valid e-mail address for <input type="email"> is deliberately narrower than RFC 5322 and describes itself as a willful violation of the RFC, because the real-world addresses browsers need to accept are simpler than the grammar allows.

What is the minimum sane pattern?

Something that rejects obvious typing errors and nothing else.

const looksLikeEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());

It requires exactly one @, no whitespace, and at least one dot after the @. That catches the mistakes people make while typing: a missing @, a space, mia@gmail, mia@@gmail.com. It rejects a handful of technically legal addresses, such as a bare local domain without a dot, which is the right trade for a public signup form.

Do not tighten it further. Patterns that enumerate top-level domains break every time ICANN adds one; patterns that forbid + reject a common Gmail convention; patterns that limit the local part to letters reject real addresses at real companies.

What can a regex not decide?

Every question that matters.

Address Regex Reality Spaw reason
[email protected] valid A typo-squat domain that may accept mail from strangers likely_typo, with did_you_mean
[email protected] valid No such domain, no mail server no_mx_records
[email protected] valid The domain publishes a null MX: it refuses all mail null_mx
[email protected] valid Gmail never issues usernames with ! invalid_local_part
[email protected] valid A disposable inbox that expires in minutes disposable
[email protected] valid A role inbox, not a person role
[email protected] valid The domain is real; the server says the mailbox is not mailbox_not_found
[email protected] at a catch-all domain valid The server accepts everything; the mailbox is unproven catch_all

The pattern is correct on every row and useless on every row. The information lives in DNS, in open lists, in provider rules and in the SMTP handshake.

What runs after the regex?

Spaw's pipeline, in the order it settles addresses.

Extraction. People paste what they have: Mia K <[email protected]>, mailto:[email protected], a value wrapped in spreadsheet quotes, an address with a trailing comma or an invisible zero-width character. Each is unwrapped before validation, and the cleaned value is echoed back so a form can show the user what was checked.

RFC 5322 parsing. What remains is parsed with a real grammar, not a pattern. Anything that fails is invalid_syntax, free, and every other field is null.

Provider username rules. Gmail, Outlook, Yahoo, iCloud, AOL and Proton each publish character sets and maximum lengths for usernames. An address that violates the rule of the provider behind its domain cannot exist and is rejected as invalid_local_part before any network work. Minimum lengths are deliberately not enforced, because legacy accounts predate them.

DNS. MX records, the address-record fallback, null MX and unresolvable targets settle whether the domain can receive mail at all.

Lists and maps. Disposable domains, role usernames and typo-squat domains.

The handshake. For everything still standing, an SMTP session asks the server about the mailbox and disconnects before any message.

curl https://spaw.co/api/v1/email \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"email": "Mia K <[email protected]>,"}'
{
  "success": true,
  "data": {
    "email": "[email protected]",
    "syntax_valid": true,
    "deliverable": "risky",
    "reason": "likely_typo",
    "did_you_mean": "[email protected]",
    "risk_score": 60,
    "// 21 more fields": "see the endpoint reference"
  },
  "meta": { "credits_used": 1, "credits_remaining": 9, "cache_hit": false }
}

The display name and trailing comma were stripped, the typo-squat was recognised, and the correction is ready to show in the form.

How should a signup form use this?

Validate in three places, each doing what it is good at.

In the browser, run the short regex on every keystroke or on blur for instant feedback, and use <input type="email"> so mobile keyboards show the @ key. Do not block submission on anything more than that.

On blur, ask the verifier. The Spaw form helper attaches to any input marked data-spaw-email, runs a lookup when the field loses focus, shows the verdict inline, offers the did_you_mean correction as a one-click fix, and never blocks the submit. It authenticates with a publishable key locked to your domain, so nothing secret ships to the page.

On the server, verify again with your secret key before creating the account, and decide policy there: reject undeliverable, ask for another address on disposable if the account has value, accept role on B2B forms, and store normalized_email so [email protected] and [email protected] cannot register twice.

What to do next

Related

markdown version: /guides/email-validation-regex-is-not-enough.md

Verify addresses the same way

The Spaw API runs every check described here on each lookup, with an SMTP handshake that never sends mail. 10 free lookups a month, no card required.

Get your API key

More guides