Regex and cron

Regex for Email Validation

A practical email address pattern that rejects the typos users actually make, with every edge case tested.

  • Free foreverNo trial, no paywall, no usage limits.
  • No sign-upOpen the page and start. We never ask for an email to use a tool.
  • PrivateEverything runs in your browser. Your input is never uploaded or stored on our servers.
  • Works offlineOnce the page has loaded, the tool keeps working without a connection.

Pattern

/^[A-Za-z0-9_%+'-]+(?:\.[A-Za-z0-9_%+'-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9-]{1,59})$/

Regular expression

JavaScript syntax, run by your browser's own engine in a background worker. Press / anywhere to jump here.
Reset to the original pattern and examplesShow cheat sheet: click any token to insert itCopy pattern
Find every match instead of stopping at the first. Needed for matchAll() and for replacing all occurrences.Case-insensitive: /abc/i also matches "ABC" and "aBc".^ and $ match at the start and end of every line, not just the whole string. Use it to test one value per line.Lets . match line breaks too. Without it, . stops at \n and \r.Treats the pattern as Unicode code points: enables \p{…} property escapes and \u{…}, makes emoji count as one character, and rejects sloppy escapes.Only matches starting exactly at lastIndex (position 0 here), never further along. Useful for tokenizers that consume input piece by piece.Records the start and end index of every capture group (match.indices). The tester always shows group positions, so this only changes your code.The upgraded unicode mode (ES2024): set operations like [\p{L}--[a-z]] and [A&&B], string properties like \p{RGI_Emoji}. Cannot be combined with u.
6 matches6 matches

Test string

Matches are highlighted as you type, in alternating colors. Click inside a match (or hover its row below) to color its capture groups.
6 matchesClear test string

Explanation

Every part of the pattern in plain English. Hover a row to highlight that part of the pattern.
Copy explanation

Loading the explainer…

Every match with its position and capture groups.Preview a replacement with $1, $<name> and $& references, then copy the result.Copy a ready-to-run snippet for JavaScript, Python, Go, C#, PHP or Dart.

Matches

Named groups are shown by name. undefined means the group did not take part in that match.
Copy matches as JSONDownload matches.csv
#MatchPosition
1jane.doe@example.com0–20
2user+newsletter@mail.example.co.uk21–55
3o'brien@example.ie56–74
4first_last@example.museum75–100
51234567@numbers.org101–120
6info@xn--bcher-kva.xn--p1ai121–148

Need a secure web app built around this? Talk to our team

Quick answer

A practical email regex is ^[A-Za-z0-9_%+'-]+(?:\.[A-Za-z0-9_%+'-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9-]{1,59})$. It accepts addresses like jane.doe@example.com, user+tag@mail.example.co.uk and Punycode domains, and rejects missing @ signs, doubled dots, hyphen-edged domain labels and one-letter top-level domains. Use it for instant form feedback, then confirm the address by sending an email: no regex can tell you a mailbox exists.

  • Tested examples
  • Token-by-token breakdown
  • Code in 5 languages
  • Live tester

How to use the email address regex

  1. 1Copy the pattern with Copy pattern in the tester, or take a ready-made snippet for your language from the code section below.
  2. 2Check the sample lines in Test string: every line the email address regex accepts is highlighted, rejected lines stay plain.
  3. 3Paste your own values one per line to see which pass, and read Explanation for what each part of the pattern does.
  4. 4Open the Code tab for JavaScript, Python, Go, C#, PHP or Dart with the pattern translated and escaped.
  5. 5Validate again on the server: client-side checks give users quick feedback, not security.

How does the email address regex work?

Read it left to right. Each piece below matches one part of the input:

  • ^ Start of the string, so nothing can come before the address.
  • [A-Za-z0-9_%+'-]+ The first part of the local name: letters, digits and _ % + ' -. The + allows plus-addressing like jane+news.
  • (?:\.[A-Za-z0-9_%+'-]+)* More dot-separated parts. Each dot must be followed by characters, so .jane, jane. and jane..doe all fail.
  • @ Exactly one at sign.
  • (?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+ One or more domain labels, each 1–63 characters, starting and ending with a letter or digit, followed by a dot.
  • (?:[A-Za-z]{2,63}|xn--[A-Za-z0-9-]{1,59}) The top-level domain: 2 to 63 letters (com, io, museum), or an internationalized one in Punycode such as xn--p1ai (.рф).
  • $ End of the string.

Which strings does the email address regex match?

The whole input must match, from the first character to the last. Every row below is checked against the pattern by our automated test suite before each release.

InputMatches?Why
jane.doe@example.comYesThe everyday case: dotted local part, ordinary domain.
user+newsletter@mail.example.co.ukYesPlus-addressing and a multi-label domain.
o'brien@example.ieYesApostrophes are legal and common in Irish and French names.
first_last@example.museumYesLong top-level domains are fine (up to 63 letters).
1234567@numbers.orgYesAn all-digit local part is valid.
info@xn--bcher-kva.xn--p1aiYesAn internationalized domain and top-level domain, in Punycode.
plainaddressNoNo @ sign.
@example.comNoEmpty local part.
jane..doe@example.comNoTwo dots in a row are not allowed in an unquoted local part.
.jane@example.comNoThe local part cannot start with a dot.
jane@exampleNoNo top-level domain. Intranet-only hosts are rejected on purpose.
jane@-example.comNoDomain labels cannot start with a hyphen.
jane doe@example.comNoSpaces are not allowed (outside rare quoted forms).
jane@example.cNoNo real top-level domain has a single letter.

Can a regex fully validate an email address?

No, and it should not try. The official grammar in RFC 5322 allows quoted local parts ("john smith"@example.com), comments in parentheses and IP-address literals (jane@[192.0.2.1]). A regex that accepts all of that is hundreds of characters long and still cannot tell you whether the mailbox exists.

This pattern takes the practical route: it accepts what real people type and rejects the typos that matter (a missing @, a doubled dot, a domain with no top-level part). Then:

  1. Trim the input and compare case-insensitively: the domain part is not case-sensitive.
  2. Send a confirmation link. That is the only check that proves the address works and belongs to the user.
  3. Do not block unusual but valid addresses at signup. A false rejection loses a real customer; a false acceptance only costs a bounced email.

What about international and unusual addresses?

Two cases deserve a decision rather than a default:

  • Internationalized addresses such as 用户@例子.广告 (RFC 6531) use non-ASCII letters. This pattern rejects them. If you serve those markets, replace the ASCII classes with [\p{L}\p{N}…] and add the u flag, or accept any local@domain and rely on the confirmation email.
  • Browser behavior: <input type="email"> uses the WHATWG pattern, which allows jane@localhost and jane..doe@example.com. Keep your server-side check at least as strict as the form, or users will see different answers in different places.

Length matters too: an address can be at most 254 characters in practice, so add a length check before running the regex on untrusted input.

How do I use the email address regex in JavaScript, Python, Go, C# and PHP?

The pattern is translated for each engine rather than pasted as is: named groups, anchors, escapes, shorthand classes and flags are adjusted so each snippet behaves as closely to the JavaScript version as its engine allows. Differences that remain (Go's RE2 has no lookarounds or backreferences, for example) are called out under each one. The Code tab in the tester above also exports Dart, and updates as you edit the pattern.

JavaScript and TypeScript

const regex = /^[A-Za-z0-9_%+'-]+(?:\.[A-Za-z0-9_%+'-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9-]{1,59})$/;

function isValid(value) {
  return regex.test(value);
}

console.log(isValid("jane.doe@example.com"));

Python

import re

pattern = re.compile(r"^[A-Za-z0-9_%+'\-]+(?:\.[A-Za-z0-9_%+'\-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})\Z")

def is_valid(value: str) -> bool:
    return pattern.search(value) is not None

print(is_valid("jane.doe@example.com"))
  • JavaScript's $ only matches at the very end of the string. Python's $ also matches before a trailing newline, so the pattern uses \Z instead.

Go

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile(`^[A-Za-z0-9_%+'\-]+(?:\.[A-Za-z0-9_%+'\-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})$`)
	fmt.Println(re.MatchString("jane.doe@example.com"))
}

C#

using System;
using System.Text.RegularExpressions;

// The timeout stops a pathological input from hanging the thread (ReDoS).
var regex = new Regex(@"^[A-Za-z0-9_%+'\-]+(?:\.[A-Za-z0-9_%+'\-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})\z", RegexOptions.None, TimeSpan.FromSeconds(1));
Console.WriteLine(regex.IsMatch("jane.doe@example.com"));
  • JavaScript's $ only matches at the very end of the string. .NET's $ also matches before a trailing newline, so the pattern uses \z instead.

PHP

<?php

$pattern = '/^[A-Za-z0-9_%+\'\-]+(?:\.[A-Za-z0-9_%+\'\-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})$/D';

function isValid(string $value, string $pattern): bool
{
    return preg_match($pattern, $value) === 1;
}

var_dump(isValid('jane.doe@example.com', $pattern));
  • The D modifier makes $ match only at the very end, like JavaScript (PCRE's $ also matches before a trailing newline).

Browse all 25 patterns in the regex pattern library, or build your own in the regex tester.

Frequently asked questions

For forms, a practical pattern like the one on this page is best: it accepts plus-addressing, apostrophes and long top-level domains, and rejects missing @ signs, doubled dots and malformed domains. Pair it with a confirmation email, because no regex can prove a mailbox exists.
Most home-made patterns forget the plus sign (jane+news@example.com), the apostrophe (o'brien@example.ie), subdomains (mail.example.co.uk) or long top-level domains like .museum. Each of those is a real customer who cannot sign up, so test against a table of real addresses before shipping.
The domain part is not. The local part technically can be, but almost every mail provider treats it case-insensitively, so store addresses as typed and compare them in lowercase.
Usually not. The full RFC 5322 grammar accepts addresses with quotes, comments and IP literals that most mail systems and databases do not expect. A practical pattern plus a confirmation email gives better results with far less risk.

Last updated by the Playxoft engineering team.

From the engineers who built this tool

Building something bigger than a quick fix?

Auth, payments, dashboards and security are where web apps get hard. We have shipped them many times. Share your idea and we will map out a realistic plan and estimate, free and with no obligation.

More free tools

See all tools

Regex Tester

Test regex live with highlighted matches, plain-English explanations and code export.

Open tool

Cron Expression Generator

New

Build and explain cron schedules, see the next runs in any time zone, copy platform snippets.

Open tool

SQL Formatter

Beautify or minify SQL in 12 dialects, with keyword case, indent and line:column errors.

Open tool

cURL to Python Converter

New

Convert cURL commands into Python requests or httpx code with params, json, files and auth.

Open tool

cURL to Fetch Converter

New

Turn any cURL command into JavaScript fetch() code with headers, JSON, forms and auth.

Open tool

cURL to Axios Converter

New

Convert cURL commands into an Axios request config with params, data, auth and timeouts.

Open tool