PatternRegex

Match an email address

A practical email regex for forms, logs and text — strict enough to catch typos, loose enough to accept real addresses like dev+ops@example.co.uk.

Last updated

Patterngi
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
Open in Regex Tester & Builder

Matches

  • ada@example.com
  • dev+ops@example.co.uk
  • grace.h_92@sub.example.io

Doesn’t match

  • plainaddress
  • name@nodot
  • @nobody.com

Why it’s written this way

The local part [a-zA-Z0-9._%+-]+ accepts the characters real providers actually allow — including + for the subaddressing Gmail and Fastmail users rely on. Patterns that forget the plus sign silently reject working addresses, and that is the most common bug in homemade email regex.

The domain side requires at least one dot and a two-plus letter ending via \.[a-zA-Z]{2,}. That rejects bare hostnames like user@localhost, which is almost always what you want in a public-facing form.

Edge cases to know

  • →It accepts consecutive dots (a..b@example.com), which are technically invalid — tightening that costs a lot of pattern for a case that barely occurs in real data.
  • →It is not RFC 5322. The full grammar allows quoted spaces and comments that no signup form should accept.
  • →The only real validation is a confirmation email. Use this to catch typos at the form, not as proof the mailbox exists.

Part by part

[a-zA-Z0-9._%+-]+
The local part, before the @. The + means one or more, and the class covers the punctuation real addresses use — including the plus that makes tagged addresses like you+shop@ work.
@
The separator. Exactly one, which is what rules out the double-@ typo that slips past looser patterns.
[a-zA-Z0-9.-]+
The domain and any subdomains. Dots are ordinary members of this class, so mail.example.co is a single match rather than three.
\.
An escaped dot separating the domain from its suffix. Unescaped it would match any character, and the pattern would accept an address with no dot at all.
[a-zA-Z]{2,}
The top-level domain: two or more letters, no upper bound. That open end is what lets .museum and .technology through.

Common mistakes

  • .+@.+

    The famously pragmatic version. It is defensible as a sanity check but it accepts "a @ b", "@@" and any string with an at-sign in it, so it catches only the user who forgot the @ entirely.

    [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
  • [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}

    Capping the suffix at three letters was reasonable when the list was .com, .org and country codes. It now rejects .info, .name, .online and every long modern TLD, and the rejection looks like a bug to the user.

  • [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$

    Anchoring only the end is the worst of both worlds: it still matches a valid address at the tail of a longer string, so "nonsense here bob@example.com" validates.

Questions

Does a match mean the address exists?

No. It confirms the shape only. An address can be perfectly formed and still be unrouteable, misspelled, or belong to someone else entirely — the only proof of existence is sending mail to it and seeing a confirmation come back.

Why not use the full RFC 5322 pattern?

Because it runs to several thousand characters, permits quoted strings, comments and bracketed IP literals that no signup form should accept, and still cannot tell you whether mail will be delivered. For a form field the trade is not worth it.

Does it accept plus-addressing like me+news@example.com?

Yes — the plus is in the local-part class deliberately. Rejecting it is a common and user-hostile bug, because plus-addressing is exactly how people tag which service leaked their address.

Should I use an input of type email instead?

For a browser form, yes, and use this alongside rather than instead. The native type gives the right mobile keyboard and a free first check; this pattern is for the places the browser cannot help, such as validating a pasted list or scanning a log.

Related in Patterns