Regex for Email, URL and Phone Number Validation
Validation regex is copied more than any other kind, and much of it is subtly wrong. Here are patterns that work in real forms, what each part does, and the cases where a regular expression is the wrong tool.
Validate loosely, verify for real
A regex can tell you that an input looks like an email address; only sending a message proves it exists. Strict patterns reject real users — plus signs, long domains, international names — while still letting typos through. Aim for patterns that catch obvious mistakes, then verify.
Email addresses
A pragmatic pattern that matches almost every real address:
^[^\s@]+@[^\s@]+\.[^\s@]{2,}$
Something, an @, a domain with a dot, and no spaces. It accepts ada+news@example.co.uk and rejects ada@example and ada @example.com. A slightly stricter version for the local part:
^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$
This one rejects internationalized addresses (like josé@example.com), so prefer the first if your users are worldwide. In HTML, <input type="email"> already validates with the browser’s own rule. To check that a domain can actually receive mail, look up its MX record with the Email DNS Checker.
URLs
Don’t write a URL regex — parse it. Every modern runtime has a URL parser that knows the rules:
function isHttpUrl(value) {
try {
const u = new URL(value);
return u.protocol === 'http:' || u.protocol === 'https:';
} catch {
return false;
}
}
Checking the protocol matters: new URL('javascript:alert(1)') parses fine, and that string must never end up in an href. If you need a regex to find URLs in text, use something simple and parse each match afterwards:
https?:\/\/[^\s<>"']+
Break a URL into protocol, host, path and query parameters with the URL Parser.
Phone numbers
Phone formats vary by country, so the safest regex only cleans input. Strip everything except digits and a leading plus, then check the length against E.164 (up to 15 digits):
const clean = input.replace(/(?!^\+)[^\d]/g, '');
const ok = /^\+?[1-9]\d{6,14}$/.test(clean);
For real validation by country — area codes, mobile vs landline — use a library such as libphonenumber. A North American–only pattern, if that is all you accept:
^\(?([2-9]\d{2})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})$
Test before you ship
Paste a pattern into the Regex Tester with a list of valid and invalid samples, one per line, and turn on the multiline flag so ^ and $ match each line. If a pattern you inherited is unreadable, the Regex Explainer breaks it down token by token. Watch for catastrophic backtracking: nested quantifiers like (a+)+ can freeze a server on a crafted input.