---
title: "An Email Regex Is Syntax, Not Validation"
description: "What an email regex can legitimately decide, the three signup questions it structurally cannot answer, and where the regex belongs in a validation pipeline."
slug: "email-regex-vs-email-validation"
date: 2026-10-03
updated: 2026-10-03
last_tested: 2026-10-03
summary: "A regex answers one question: does the string parse? Domain existence, typo intent, and disposable policy need DNS and curated data. Keep the regex client-side and let an API decide."
cluster: "Validation engine"
intent: "engineering"
sources:
  - title: "RFC 5322 — Internet Message Format"
    url: "https://www.rfc-editor.org/rfc/rfc5322.html"
  - title: "RFC 5321 — Simple Mail Transfer Protocol"
    url: "https://www.rfc-editor.org/rfc/rfc5321.html"
  - title: "RFC 7505 — A Null MX Resource Record"
    url: "https://www.rfc-editor.org/rfc/rfc7505.html"
  - title: "nobounce.dev verdict taxonomy"
    url: "https://nobounce.dev/v1/config"
  - title: "nobounce.dev OpenAPI specification"
    url: "https://nobounce.dev/openapi.json"
  - title: "MDN — Regular expressions guide"
    url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions"
---

Every email validation conversation eventually arrives at the regex. The honest version of the answer is narrow: a regex decides whether a string *parses* as an address, and nothing else. A signup flow needs four answers — does it parse, does the domain accept mail, is it what the user meant to type, and do you want it in your database. The regex covers the first question, where it is also the cheapest tool. The other three require DNS lookups and curated datasets, and no amount of pattern cleverness substitutes for them.

## What a regex can legitimately decide

RFC 5322 defines the address grammar, and the full grammar is famously unimplementable in a practical pattern: quoted local parts, nested comments, folding whitespace. Nobody's signup form needs to accept `"john..doe"@example.com`, and nobody's regex does. What production systems check is RFC 5322 *as practised* — a pragmatic subset that accepts every address a real user types and rejects the mangled ones:

```js
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
```

This catches the mechanical failures: `not-an-email`, `two@@gmail.com`, `spa ce@gmail.com`. nobounce's own first engine layer is exactly this kind of practical syntax check — one of several reasons it rejects `user@nodots`, a dotless domain, before ever touching DNS:

```json
{
  "verdict": "undeliverable",
  "reason": "syntax_invalid",
  "checked": { "syntax": true, "mx": false, "typo": true, "disposable": true }
}
```

Note `checked.mx: false`: a syntax rejection costs no DNS query, and the response says so explicitly.

If you keep a regex at all, keep it here — a client-side pre-check that prevents an obviously malformed submit from wasting a round trip. Do not chase the grammar. The edge cases the regex gets wrong are exactly the ones that don't matter, and the ones it gets right are the ones the API's first layer already decides.

## The three questions a regex structurally cannot answer

**Does the domain accept mail?** `user@gmail.cm` and `user@gmail.com` both match any sane regex. One is an NXDOMAIN and a dead signup. The answer lives in DNS — MX records per RFC 5321, the null-MX convention of RFC 7505, and the difference between a domain that does not exist and a resolver that failed. This is a whole engineering topic on its own, covered in [Why MX Validation Misses Typo Domains](https://nobounce.dev/blog/mx-validation-misses-typo-domains/).

**Is it what the user meant?** A transposed `gmial` parses perfectly. This is the question regex-based validation silently fails, and it is the one that costs signups: rejecting `user@gmai.com` loses a real user, and accepting it hands the address to a typosquat operator. These are live results from the API:

| input | regex says | nobounce says |
|---|---|---|
| `user@gmail.cm` | accepts | `undeliverable`, `domain_not_found`, suggests `user@gmail.com` |
| `user@gmai.com` | accepts | `undeliverable`, `typosquat_mx`, suggests `user@gmail.com` |
| `user@yahooo.com` | accepts | `risky`, `likely_typo`, suggests `user@yahoo.com` |
| `user@gmial.com` | accepts | `unknown`, `dns_error` — fails open, still suggests |

The correction runs before DNS because the typo'd domains frequently resolve anyway; `gmai.com` publishes MX records pointing at `mail.h-email.net`. A DNS-only pipeline accepts it. And when the resolver itself fails — `gmial.com` is a live SERVFAIL — the API reports `unknown` with `checked.mx: false` instead of inventing a verdict, a split detailed in [SERVFAIL vs NXDOMAIN in Email Validation](https://nobounce.dev/blog/servfail-vs-nxdomain-email-validation/). A regex has no equivalent of either answer; its output space is simply accepts/rejects.

**Do you want it?** `user@mailinator.com` parses. `admin@gmail.com` parses. Whether disposable domains and role accounts belong in *your* database is a policy question with a policy-shaped answer — verdict `risky` with the reason naming which case it is — not a pattern question.

## Where the regex belongs in the pipeline

Two layers, each doing the job it is suited for:

1. **Client-side regex** — reject `foo` and `foo@` before submit. Cheap, instant, offline.
2. **Server-side API call on submit** — the authority. Live DNS, typo correction with a suggestion, and the policy verdicts, all fail-open.

```bash
curl -X POST https://nobounce.dev/v1/check \
  -H "Authorization: Bearer $NOBOUNCE_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"email":"user@gmai.com"}'
```

The key travels in the `Authorization` header, never in a URL. Live checks are paid, from $1/mo — there is no free tier — while `POST /demo/check` resolves a frozen fixture corpus with no key so you can see every verdict and reason shape before paying. It cannot validate arbitrary live addresses.

The integration rules that make the API call worth having: any non-null `suggestion`, including a `risky` + `likely_typo` verdict, means offer the correction rather than reject; and the call itself must fail open so validation never becomes a hard dependency of signup. Those mechanics are covered in [Suggest the Correction Instead of Rejecting the Signup](https://nobounce.dev/blog/suggest-the-correction-not-rejection/).

## The honest limit on both sides

A regex cannot tell you the domain exists. It also cannot tell you the mailbox exists — but neither can a DNS check. Confirming a specific mailbox would need SMTP RCPT TO probing, which nobounce never performs: it demands IP reputation to be accurate and catch-all domains at the large providers make the signal weak. That work is permanently out of scope, and any service advertising certainty about mailbox existence from pattern matching or DNS alone is overselling. Confidence is a number, not a promise, and `unknown` is a verdict, not an error.

So keep the regex. Give it the one job it can do — catching mechanical garbage at the keystroke layer — and hand the other three questions to something that can actually answer them.

For the full key-acquisition, verdict-handling, and verification procedure, give your coding agent [/integrate.md](https://nobounce.dev/integrate.md).
