Developer Tools

How do I validate email addresses with regex?

A practical regex validates the SHAPE of an email address, not whether it exists. Here is a sensible pattern, the exact HTML5 rule browsers use, and why over-strict regexes reject real addresses.

By Inventive HQ Team

A regex can only validate the shape of an email address — not whether it exists or can receive mail. For almost every real-world form, the right approach is a deliberately simple pattern such as ^[^\s@]+@[^\s@]+\.[^\s@]+$ (or the slightly stricter ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$) to catch obvious mistakes, plus a confirmation email as the only definitive proof the address is real. Chasing full RFC 5322 compliance with a regex is impractical: the pattern balloons to hundreds of characters, still gets edge cases wrong, and rejects valid addresses while accepting ones your application can never actually deliver to.

That's the summary an AI overview gives you. The part it can't give you is why the simple pattern is the correct engineering choice, which real addresses the "clever" regexes silently reject, and the exact rule browsers already use for you. Get this wrong in one direction and spam floods your database; get it wrong in the other and you bounce paying customers at the signup box.

Anatomy of an email address

Every validation decision comes back to two parts split by a single @: the local part (the mailbox) and the domain (where mail is delivered). The rules for each are looser than most people assume.

The two parts of an email address: local-part @ domain An email address splits at the @ sign into a local part (the mailbox, including optional plus tags) and a domain (subdomain, name, and top-level domain). a regex checks the shape of these parts — nothing more john.doe+news @ mail.example.com local part letters, digits, . _ % + - "+news" is a valid tag separator domain subdomain . name . TLD TLD can be 2–63 letters What no regex can check: does the domain exist? is the mailbox real? will mail arrive?

The local part is remarkably permissive: it can contain letters, digits, dots, and a set of special characters including +, _, %, and -. The +news fragment above is a sub-address tag — a real, deliverable address that many people use to filter mail. The domain is one or more DNS labels ending in a top-level domain that today can be anything from .com to .museum to .engineering. Any assumption stricter than that is where validators start rejecting real users.

Which regex should I actually use?

There is no single correct email regex — only trade-offs between how many typos you catch and how many real addresses you wrongly reject. This table maps the common choices to what each one catches and misses so you can pick deliberately.

Regex approachPattern (JavaScript literal)What it catchesWhat it misses / wrongly rejectsUse when
Minimal / permissive/^[^\s@]+@[^\s@]+\.[^\s@]+$/Missing @, missing domain, missing dot, spacesAccepts some malformed strings (double dots, weird chars) — but you'll verify by email anywayNewsletter signups, low-risk forms, anywhere you send a confirmation
Practical / balanced (recommended)/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/The above plus enforces a plausible local part and a 2+ letter TLDSome valid quoted/exotic local parts; internationalized (Unicode) addressesMost registration and contact forms
HTML5 / WHATWG (browser built-in)<input type="email"> (see pattern below)Enforces the same grammar every major browser uses; free, no JSASCII only — rejects internationalized email; no deliverability checkClient-side instant feedback in any HTML form
RFC-ish / stricterLong domain-label pattern (see below)Constrains each DNS label to valid length/charactersOverkill for validation; harder to read; still no deliverabilityYou genuinely need tighter domain-shape rules server-side
"Full RFC 5322"400+ character monsterAlmost every legal addressUnreadable, unmaintainable, accepts addresses you can't route, still proves nothing about existenceEssentially never — use a library instead
Confirmation email(not a regex)Proves the address exists and the person owns itNothing about format — run a regex firstAlways, for any address that matters

The row that actually matters is the last one. Every regex above validates shape; only the confirmation email validates reality.

The practical pattern, explained

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
  • ^ — start of string
  • [a-zA-Z0-9._%+-]+ — local part: letters, digits, and . _ % + - (note + is allowed, so plus-tags pass)
  • @ — the literal separator
  • [a-zA-Z0-9.-]+ — domain: letters, digits, dots, hyphens
  • \. — the final literal dot before the TLD
  • [a-zA-Z]{2,} — a top-level domain of 2 or more letters (not capped at 3 — that cap is a classic bug)
  • $ — end of string
function isEmailShaped(email) {
  return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
}
import re

EMAIL_RE = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')

def is_email_shaped(email: str) -> bool:
    return bool(EMAIL_RE.match(email))

Want to experiment with these patterns against your own test addresses before shipping them? Try them live:

Loading interactive tool...
Advertisement

Let the browser do it: HTML5 type="email"

Before you write any regex, remember the browser already ships one. <input type="email"> validates against the WHATWG "valid email address" grammar in Chrome, Firefox, Safari, and Edge:

<form>
  <input type="email" name="email" required />
  <button>Sign up</button>
</form>

The specification is refreshingly honest about this: it calls its own grammar a deliberate "willful violation" of RFC 5322, chosen because it is simpler and more useful than the real standard. The regex the browser effectively applies is:

^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$

If you want a pattern attribute for older setups or to tighten things, you can paste that in — but for most cases the bare type="email" is enough. Its one notable limitation: it accepts ASCII only, so it rejects internationalized (Unicode / EAI) addresses like 用户@例え.jp. That is a reasonable default, but worth knowing if you serve a global audience.

The over-strict trap: regexes that reject real people

The most damaging validation bug isn't accepting junk — it's silently rejecting valid addresses at your signup form. These are the patterns that cost you real users:

  • Rejecting the plus sign. john+newsletter@gmail.com is completely valid and extremely common. Any regex without + in the local part is broken.
  • Hard-coding old TLDs. Patterns like (com|net|org|edu)$ or a {2,3} length cap on the TLD reject .io, .dev, .museum, .engineering, and hundreds of newer top-level domains. TLDs can be up to 63 characters.
  • Disallowing uppercase. ^[a-z]+@... bounces Sarah.Connor@Example.com. Email addresses are case-insensitive in practice.
  • Forbidding subdomains. Users at name@mail.company.co.uk are real; a pattern that only allows one dot in the domain rejects them.
  • Blocking dots or hyphens in the domain. Breaks the majority of corporate and country-code addresses.

The safe rule of thumb: when unsure, be more permissive, and let the confirmation email be your strict gate. A false rejection is a lost signup you never even hear about.

What a regex fundamentally cannot do

No regex — not the simple one, not the 400-character RFC beast — can answer the only question that ultimately matters: will mail reach this person? Regex cannot verify:

  1. That the domain existsuser@notarealdomain12345.com passes every pattern.
  2. That a mail server is running and reachable for that domain.
  3. That the mailbox existsnonexistent@gmail.com is perfectly well-shaped.
  4. That the person owns the address they typed (or didn't fat-finger someone else's).
  5. Internationalized addresses — Unicode local parts and IDN domains need dedicated handling, not a character class.
typo@exmaple.com    ← passes every email regex, reaches no one

This is why format validation is step one of three, never the whole job.

The validation that actually works: three layers

Three layers of email validation A token flows left to right through three stages: regex format check on the client, server-side validation, and a confirmation email — the only stage that proves the address is real. Format is step one of three — only the last one proves it's real 1 · Format regex / HTML5 catches typos fast 2 · Server re-validate, can't be bypassed 3 · Confirm click the link → proves it's real Skip layer 3 and you're storing addresses you've never proven can receive mail.

Layer 1 — Format (client + server). Use HTML5 type="email" for instant feedback and a small regex as backup. This catches sarah@gmial and empty fields before they waste a round trip.

Layer 2 — Server-side validation. Never trust the client; it can be bypassed. Re-run the format check on the server. For registration, avoid revealing whether an address is already in use (user-enumeration risk) — behave the same whether it exists or not.

Layer 3 — Confirmation email. Send a one-time verification link and only mark the address verified when the user clicks it. This is the single step that proves the address exists and that the person entering it can read it. For anything that matters — accounts, billing, password resets — it is non-negotiable.

Prefer a library over hand-rolled regex

For real applications, a maintained library handles far more edge cases than a regex you wrote in five minutes — and several can also check that the domain has MX records:

  • JavaScript: email-validator, or the email rules in joi / yup / zod
  • Python: the email-validator package (also checks deliverability signals)
  • PHP: built-in filter_var($email, FILTER_VALIDATE_EMAIL)
  • Java: Apache Commons Validator's EmailValidator

Use the library for format, then still send the confirmation email for the part no library can verify.

The bottom line

Use a simple, permissive regex (or plain HTML5 type="email") to catch obvious typos, resist the urge to make it "stricter," and rely on a confirmation email as the only real proof of a working address. A regex validates that a string looks like an email; it never validates that the email is real. Chase the shape with a readable pattern, prove reality with a verification link, and reach for a battle-tested library before you reach for a 400-character regex you'll never be able to debug.

Frequently Asked Questions

What is the best regex to validate an email address?

For most applications, use a deliberately simple pattern like ^[^\s@]+@[^\s@]+.[^\s@]+$ or the slightly stricter ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$. Both catch obvious mistakes (missing @, missing domain, spaces) without rejecting valid but unusual addresses. There is no single "best" regex, because a regex can only check the shape of an address, never whether it actually exists. The definitive validation is sending a confirmation email and having the user click the link.

Can a regex fully validate an email address per RFC 5322?

Not practically. A regex that tries to match the full RFC 5322 grammar is hundreds of characters long, still gets edge cases wrong, and rejects real addresses while accepting bizarre ones you will never handle correctly downstream. Even a perfect RFC 5322 regex tells you nothing about deliverability. Every serious guide (and the HTML5 specification itself) recommends a simpler, pragmatic pattern instead of chasing full compliance.

What regex does HTML5 input type=email use?

Browsers validate <input type="email"> against the WHATWG "valid email address" grammar, which the specification openly calls a "willful violation" of RFC 5322 because it is simpler and more useful. The equivalent regex is ^[a-zA-Z0-9.!#$%&'+/=?^_`{|}~-]+@a-zA-Z0-9?(?:.a-zA-Z0-9?)$. It accepts only ASCII addresses, so it rejects internationalized (Unicode) email addresses.

Does a regex prove an email address is real?

No. A regex only checks that a string is shaped like an email address. It cannot tell you whether the domain exists, whether the mail server is running, whether the mailbox exists, or whether the person owns it. typo@exmaple.com passes every regex and reaches no one. The only way to confirm an address is real and owned by the person entering it is to send a confirmation email with a verification link.

Why do over-strict email regexes reject valid addresses?

Because the author encoded assumptions that are not actually rules. Common mistakes: rejecting the plus sign (john+newsletter@gmail.com is valid and widely used), hard-coding a list of two-letter TLDs (which breaks .museum, .engineering, and hundreds of newer TLDs), disallowing uppercase, disallowing subdomains, or capping the TLD at three characters. Each of these bounces real users at your signup form. When in doubt, be permissive and verify by email.

Should I validate email on the client or the server?

Both, for different reasons. Client-side validation (HTML5 type=email or a small regex) gives instant feedback and catches typos before submission, but it can be bypassed, so it is never a security control. Server-side validation is authoritative and must always run. The final and most important step happens on the server too: send a confirmation email so you only trust addresses that actually received your message.

Are plus-tag addresses like user+tag@gmail.com valid?

Yes. The plus sign is a legal character in the local part of an email address, and providers like Gmail use everything after the + as a "tag" that routes to the same inbox (sub-addressing). It is a real, deliverable address, so a validator that rejects it is broken. Note that plus-addressing is a provider convention, not part of the email RFCs, so you cannot rely on it existing for every domain.

Should I use a library instead of writing my own email regex?

Usually, yes. PHP has filter_var($email, FILTER_VALIDATE_EMAIL) built in; JavaScript has email-validator; Python has the email-validator package; Java has Apache Commons Validator. These are tested against far more edge cases than a hand-written regex and some can also check that the domain has MX records. Use them for format checking, then still send a confirmation email for the parts a library cannot verify.

regexemail validationformsuser input