Email regex with a live tester
There is no single correct email regex; the most practical choice is the pattern the HTML standard defines for input type=email, which every modern browser enforces. Below: that regex, copied from the HTML Standard, a live tester with 16 edge cases, a simple and an RFC 5322-style alternative, and copy-paste code for HTML, JavaScript, Python and PHP.
/^[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])?)*$/8 of 16 lines match this pattern.
| Input (match highlighted) | This regex | type=email | RFC 5321 lengths |
|---|---|---|---|
[email protected] | ✓ Match | ✓ Valid | ✓ OK |
[email protected] | ✓ Match | ✓ Valid | ✓ OK |
o'[email protected] | ✓ Match | ✓ Valid | ✓ OK |
[email protected] | ✓ Match | ✓ Valid | ✓ OK |
test@localhost | ✓ Match | ✓ Valid | ✓ OK |
[email protected] | ✓ Match | ✓ Valid | ✓ OK |
[email protected] | ✓ Match | ✓ Valid | ✓ OK |
jane [email protected] | ✗ No match | ✗ Invalid | ✓ OK |
[email protected] | ✗ No match | ✓ Valid | ✓ OK |
jane@bücher.de | ✗ No match | ✓ Valid | ✓ OK |
jösé@example.com | ✗ No match | ✗ Invalid | ✓ OK |
"john doe"@example.com | ✗ No match | ✗ Invalid | ✓ OK |
[email protected] | ✗ No match | ✗ Invalid | ✓ OK |
[email protected]. | ✗ No match | ✗ Invalid | ✓ OK |
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@example.com | ✓ Match | ✓ Valid | ✗ Too long |
@example.com | ✗ No match | ✗ Invalid | ✓ OK |
Runs in your browser; nothing is sent or stored. The type=email column mirrors Chromium when a person types the value: it trims surrounding spaces and converts an IDN domain to punycode before applying the WHATWG rule.
Paste up to 100 addresses, one per line or comma-separated. Each one gets a format check (the HTML email rule plus RFC 5321 lengths), a typo check against major providers, a disposable-domain check against 5,438 known domains, and a role-address flag.
Runs in your browser: addresses never leave this page. There is no SMTP probe and no MX lookup, so a pass means the address is well-formed and its domain is not a known disposable service, not that the inbox exists.
Key facts
| Question | Answer | Source |
|---|---|---|
| Rule browsers apply to type=email | The WHATWG "valid email address" definition | HTML Standard |
| Dot required after the @? | No: test@localhost is valid | HTML Standard |
| Longest part before the @ | 64 octets | RFC 5321 §4.5.3.1.1 |
| Longest address | 254 octets (256-octet path minus the angle brackets) | RFC 5321 §4.5.3.1.3 |
| Longest domain label | 63 characters | RFC 1034 §3.5 |
| How browsers run pattern= | Anchored as ^(?:…)$, compiled with the v flag; an invalid pattern is ignored | HTML Standard |
| IDN domain typed into type=email | Converted to punycode, then validated (verified in Chromium 152) | HTML Standard, Email state |
| Disposable-domain list used here | 5,438 domains, snapshot dated May 7, 2026 | disposable-email-domains |
Which email regex should I use?
Use the WHATWG pattern from the HTML Standard. It is the rule <input type="email"> applies, so a server check with the same regex agrees with the browser, and it accepts the addresses people actually use: plus-addressing, apostrophes and subdomains. It does not check length, so add the RFC 5321 limits in code: 64 octets before the @ and 254 for the whole address.
| Pattern | Regex | Accepts | Rejects | Use when |
|---|---|---|---|---|
| WHATWG (HTML standard)Source: HTML Standard, valid email address | /^[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])?)*$/ | Letters, digits and .!#$%&'*+/=?^_`{|}~- before the @; hostname labels after it. Accepts test@localhost. | Spaces, quoted local parts, Unicode, IP literals, hyphen-led domain labels, trailing dots in the domain. | Default choice: it is the rule every browser applies to input type=email, so client and server agree. |
| Simple practicalNot from a standard | /^[^\s@]+@[^\s@]+\.[^\s@]+$/ | Anything without spaces or a second @, with a dot after the @. Accepts Unicode and IDN domains. | Spaces, missing @, a domain without a dot (test@localhost). | A quick typo guard where you would rather accept odd addresses than block a real one. |
| RFC 5322-style (addr-spec)Source: RFC 5322 §3.4.1 | /^(?:[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\t !#-\[\]-~]|\\[\t -~])*")@(?:[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|\[[\t -Z^-~]*\])$/ | Quoted local parts ("john doe"@…), IP and bracket literals, dotless domains, symbols such as - or ! as a whole domain. | Leading, trailing or doubled dots in the local part, Unicode, unquoted spaces. | Rarely on a web form: it accepts addresses browsers reject and domains that cannot receive mail. |
The WHATWG regex is copied verbatim from the HTML Standard. The RFC 5322-style pattern was written for this page from the section 3.4.1 grammar, without comments, folding whitespace or obsolete syntax.
What regex does input type=email use?
<input type="email"> uses the "valid email address" rule from the HTML Standard. The rule allows letters, digits and .!#$%&'*+/=?^_`{|}~- before the @, and hostname labels of up to 63 characters after it, and it does not require a dot in the domain, so test@localhost is valid. The standard gives this JavaScript-compatible regex for it: /^[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])?)*$/
Before the rule runs, the browser strips line breaks and leading and trailing whitespace from the value, which is why " [email protected] " passes in the field but fails the raw regex (Email state; MDN). With the multiple attribute, every comma-separated address has to pass. The HTML input reference shows type=email in a working form.
Which addresses does each pattern accept?
This matrix runs 16 edge cases through what type="email" accepts when the value is typed, the three raw regexes, and the RFC 5321 length limits. It is generated from the same code as the tester above.
| Input | Case | Browser type=email | WHATWG regex (raw) | Simple regex | RFC 5322 regex | RFC 5321 lengths |
|---|---|---|---|---|---|---|
[email protected] | Plain address | ✓ Valid | ✓ Match | ✓ Match | ✓ Match | ✓ Within |
[email protected] | Plus-addressing | ✓ Valid | ✓ Match | ✓ Match | ✓ Match | ✓ Within |
o'[email protected] | Apostrophe | ✓ Valid | ✓ Match | ✓ Match | ✓ Match | ✓ Within |
[email protected] | Subdomain | ✓ Valid | ✓ Match | ✓ Match | ✓ Match | ✓ Within |
test@localhost | No dot in domain | ✓ Valid | ✓ Match | ✗ No match | ✓ Match | ✓ Within |
[email protected] | Trailing dot before @ | ✓ Valid | ✓ Match | ✓ Match | ✗ No match | ✓ Within |
[email protected] | Double dots | ✓ Valid | ✓ Match | ✓ Match | ✗ No match | ✓ Within |
jane [email protected] | Space inside | ✗ Invalid | ✗ No match | ✗ No match | ✗ No match | ✓ Within |
[email protected] | Leading/trailing spaces | ✓ Valid | ✗ No match | ✗ No match | ✗ No match | ✓ Within |
jane@bücher.de | IDN domain | ✓ Valid | ✗ No match | ✓ Match | ✗ No match | ✓ Within |
jösé@example.com | Unicode before @ | ✗ Invalid | ✗ No match | ✓ Match | ✗ No match | ✓ Within |
"john doe"@example.com | Quoted local part | ✗ Invalid | ✗ No match | ✗ No match | ✓ Match | ✓ Within |
[email protected] | Hyphen-led domain label | ✗ Invalid | ✗ No match | ✓ Match | ✓ Match | ✓ Within |
[email protected]. | Trailing dot in domain | ✗ Invalid | ✗ No match | ✓ Match | ✗ No match | ✓ Within |
aaaaaaaaaa…[email protected] | 65-character local part | ✓ Valid | ✓ Match | ✓ Match | ✓ Match | ✗ Too long |
@example.com | Nothing before @ | ✗ Invalid | ✗ No match | ✗ No match | ✗ No match | ✓ Within |
Browser column: what a person sees when typing the value into <input type="email"> in Chromium 152, verified on October 2, 2026. The browser trims surrounding spaces and converts an IDN domain such as bücher.de to punycode (xn--bcher-kva.de) before applying the WHATWG rule, so it can accept input the raw regex rejects. Setting input.value from JavaScript skips the punycode step, and other browsers can differ on IDN.
- The WHATWG rule accepts [email protected] and [email protected], which RFC 5322 does not allow, and test@localhost, which can't receive mail from the internet.
- The simple pattern accepts [email protected] and [email protected]., which browsers reject: a domain label can't start with a hyphen, and the WHATWG rule allows no trailing dot.
- No regex here enforces the 64-octet limit before the @: the 65-character case passes all three.
How do I use the email regex in HTML, JavaScript, Python and PHP?
Use type="email" in HTML and the same WHATWG rule on the server: RegExp.test in JavaScript, re.fullmatch in Python, and filter_var in PHP, which needs no regex. Trim the value first, as browsers do, and check the RFC 5321 lengths in code. For client-side error messages, see how to validate an HTML form with JavaScript.
HTML: type=email plus a pattern that requires a dot
<!-- type="email" already applies the WHATWG rule. The pattern adds one
more: a dot after the @, so test@localhost is rejected. -->
<label for="email">Email</label>
<input id="email" type="email" name="email" autocomplete="email"
required maxlength="254"
pattern="[^@\s]+@[^@\s]+\.[^@\s]+"
title="Enter an address like [email protected]">Browsers anchor pattern as ^(?:…)$ and compile it with the v flag (HTML Standard). Validate again on the server: anyone can bypass the browser.
HTML: the WHATWG rule as a pattern (escaped for the v flag)
<!-- For a type="text" field. The raw WHATWG regex does not compile with
the v flag browsers use for pattern, so { | } and - are escaped. -->
<input type="text" name="email" inputmode="email" autocomplete="email"
pattern="[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])?)*">Only needed on a type="text" field; type="email" already applies this rule. Under the v flag, { } | and - must be escaped inside square brackets.
JavaScript: RegExp.test plus the RFC 5321 length limits
const EMAIL_RE =
/^[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])?)*$/;
function isValidEmail(value) {
const email = value.trim();
const local = email.slice(0, email.lastIndexOf("@"));
return email.length <= 254 && local.length <= 64 && EMAIL_RE.test(email);
}Trimming matches what the browser does to the value. Everything the regex allows is ASCII, so .length counts octets.
Python: re.fullmatch plus the length limits
import re
EMAIL_RE = re.compile(
r"[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])?)*"
)
def is_valid_email(value: str) -> bool:
email = value.strip()
local, _, _ = email.rpartition("@")
return (
len(email) <= 254
and len(local) <= 64
and EMAIL_RE.fullmatch(email) is not None
)re.fullmatch anchors both ends, so the pattern has no ^ or $.
PHP: filter_var with FILTER_VALIDATE_EMAIL (no regex)
<?php
function is_valid_email(string $value): bool
{
return filter_var(trim($value), FILTER_VALIDATE_EMAIL) !== false;
}No regex needed. Tested on PHP 8.3.14: rejects test@localhost and [email protected], accepts user@[192.168.0.1]. Add FILTER_FLAG_EMAIL_UNICODE to accept Unicode before the @ (PHP manual).
Why doesn't the email regex work in a pattern attribute?
Browsers compile pattern with the RegExp v flag, and the raw WHATWG regex is invalid under it: inside a character class the v flag requires { } | and - to be escaped, so compiling it throws a SyntaxError ("Invalid character in character class" in Chromium). The HTML Standard says a pattern that fails to compile is ignored, so the field enforces nothing extra and shows no error. Escape those four characters, as in the HTML snippet on this page, and paste your pattern into the tester, which reports whether it compiles with v.
The HTML Standard compiles the attribute with RegExpCreate(pattern, "v"), then again as ^(?:pattern)$, and gives the element no pattern at all if either step fails (the pattern attribute). Under the v flag, ( ) [ ] { } / - \ | must be escaped inside a character class, and doubled punctuation such as && is reserved. Browsers are encouraged to log the error to the developer console, so check there when a pattern seems to do nothing.
Why not use a full RFC 5322 email regex?
Because RFC 5322 describes message headers, not what a sign-up form should accept. Its addr-spec grammar (section 3.4.1) allows quoted local parts with spaces, bracketed domain literals and domains such as a bare hyphen, yet rejects [email protected], which browsers accept. Length limits come from RFC 5321, so no RFC 5322 regex enforces them either. The HTML Standard calls its own email rule a "willful violation of RFC 5322" for these reasons.
The RFC 5322-style pattern on this page follows the addr-spec grammar: a dot-atom or quoted string, an @, then a dot-atom or a bracketed domain literal:
/^(?:[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\t !#-\[\]-~]|\\[\t -~])*")@(?:[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|\[[\t -Z^-~]*\])$/It accepts "john doe"@example.com, user@[192.168.0.1] and a@-, and it rejects [email protected], which a browser accepts, so a form using it disagrees with its own email field. Keep it for parsing mail headers, not for web forms.
What is the maximum length of an email address?
254 octets, which is 254 characters for an ASCII address. RFC 5321 caps a path at 256 octets including the angle brackets around the address, leaving 254, and caps the part before the @ at 64 octets. RFC 1034 limits each domain label to 63 characters. The WHATWG regex enforces the 63-character label limit but not the 64 or 254 limits, so check those in code.
Sources: RFC 5321 §4.5.3.1.1 (local part), §4.5.3.1.3 (path) and RFC 1034 §3.5 (labels). On the input, maxlength="254" stops longer addresses being typed at all.
Does input type=email accept international (IDN) domains?
Yes, when a person types one. Chromium converts the domain to punycode before validating, so jane@bücher.de becomes [email protected] and passes (verified in Chromium 152 on October 2, 2026). The raw WHATWG regex rejects the Unicode form, and setting input.value from JavaScript skips the conversion, so validate the punycode value on the server. Unicode before the @, as in jösé@example.com, is rejected.
The HTML Standard says browsers should convert punycode in the domain to IDN for display and back again (Email state). On the server, convert with your language's IDNA library (in JavaScript, new URL("http://" + domain).hostname returns the punycode form) before running the regex.
Does an email regex check that the address exists?
No: a regex checks shape, not deliverability. It can't tell whether the domain accepts mail, whether the mailbox exists, or whether gmial.com was meant to be gmail.com. The only proof an inbox exists is a message it receives, such as a confirmation link. The Check addresses tab on this page adds typo, disposable-domain and role-address checks, all run in your browser.
An email address checker tests whether an address is well-formed, whether its domain looks like a typo of a major provider, and whether that domain is a known disposable-mail service; it cannot prove the inbox exists. Open the Check addresses tab to run all four checks on up to 100 addresses and download the results as CSV.
What each check means
| Check | What it tells you | What it can't tell you |
|---|---|---|
| Format | The address matches the HTML email rule (after converting an IDN domain to punycode, as Chromium does) and the RFC 5321 limits of 64 and 254 octets. Dotless domains and stray dots before the @ are flagged as unusual. | Whether the domain exists or accepts mail. |
| Typo | The domain is one or two edits (a swapped pair of letters counts as one) from a major provider such as gmail.com or outlook.com, or ends in a TLD typo such as .con. Some typo domains, gmial.com among them, are also on the disposable list. | Whether the look-alike is somebody's real domain; treat it as a suggestion. |
| Disposable | The domain, or a parent domain, is on the 5,438-domain disposable-email-domains list (snapshot dated May 7, 2026), the same list splitforms uses to score submissions. | Throwaway domains created after the snapshot. |
| Role address | The part before the @ is a shared mailbox name such as admin, info, sales, support or noreply. | Whether a person reads it; role addresses are fine for most contact forms. |
Email validation vs verification
Validation checks the text: format, length, likely typos and known throwaway domains, all without contacting anyone. Verification proves a real inbox receives mail, and the dependable way to do that is to send a message with a confirmation link and wait for the click. The PHP manual makes the same point under FILTER_VALIDATE_EMAIL. Validate on the form, verify where a wrong address costs you something, such as account sign-up or a paid order.
The disposable list is the open-source disposable-email-domains project (5,438 domains, snapshot dated May 7, 2026). It loads into your browser only when you run a check.
After the regex
Let the form endpoint catch what a regex can't
Point the form at splitforms: every submission that passes smart spam filtering is saved to your dashboard and emailed to you. Free for 500 submissions total, no server code.
- Honeypot and time-trap hits are dropped. A filled hidden
botcheckfield, or a submission sent less than 2 seconds after the form loaded (when the form sendsform_loaded_at), never reaches you. Make one with the honeypot generator. - Content-rule hits go to your spam folder. Rules such as link-domain count, repeated links, spam words, disposable-email domains, malformed email, gibberish, suspicious TLDs and hosts, HTML and BBCode links, and attack probes add points; a submission at 4 points or more is quarantined, not deleted.
- Disposable domains count more than malformed addresses. An address on a known disposable domain is enough on its own to send a submission to spam; a malformed email field is not. Some typo domains are on the list too (gmial.com and hotmial.com are), so a lead who mistypes gmail.com that way lands in the spam folder, where you can still find it.
- Pro is $5/mo for 5,000 submissions a month, or $49/year on the Annual plan, when you outgrow the free plan.
More email regex questions
Is test@localhost a valid email address?
For input type=email, yes. The WHATWG rule needs at least one domain label after the @ but no dot, so test@localhost passes the browser check, though it can't receive mail from the internet. To require a dot, add pattern="[^@\s]+@[^@\s]+\.[^@\s]+" to the input; the browser keeps its own email check and adds that one rule.
How do I validate an email address in Python?
Compile the WHATWG regex and call re.fullmatch on the stripped value, which anchors both ends, so the pattern needs no ^ or $. Then check the lengths: at most 254 characters in total and 64 before the last @. The Python snippet on this page does both.
Should I use a regex or filter_var in PHP?
Use filter_var($email, FILTER_VALIDATE_EMAIL). The PHP manual says it checks RFC 822 addr-spec syntax without comments, whitespace folding or dotless domains. Tested on PHP 8.3.14, it rejected test@localhost, [email protected] and a 65-character local part, and accepted user@[192.168.0.1]. Add FILTER_FLAG_EMAIL_UNICODE (PHP 7.1+) to accept Unicode before the @.
Is the plus sign allowed in an email address?
Yes. + is one of the characters RFC 5322 allows before the @, and the WHATWG rule accepts it, so [email protected] is valid. People use it to sort incoming mail, so a regex that rejects + blocks real addresses.
Does splitforms validate email addresses?
It scores them as spam signals instead of rejecting submissions. An email field that fails a strict format check is one signal and needs another before a submission goes to the spam folder; an address on a known disposable-email domain is enough on its own. The visitor never sees an error, and you can review the spam folder in the dashboard.