Regex Cheat Sheet: Syntax, Flags and 20 Ready-to-Use Patterns
A regular expression (regex) is a pattern that describes text you want to find, check or replace. The core of any regex cheat sheet fits in one line: \d is a digit, \w is a letter, digit or underscore, \s is whitespace, . is any character except a line break, * + ? say how many times, ^ and $ anchor to the start and end, ( ) make groups, and [ ] make character sets.
The tables below cover that syntax and the JavaScript flags, and the 20 patterns after them are ready to paste. Every example on this page was run in JavaScript (Node.js 22) before publishing, and you can try any pattern in our Regex Tester, which uses the browser's native JavaScript regex engine. Other languages differ in small ways, which the FAQ covers.
Regex Syntax Cheat Sheet
Character Classes
| Pattern | Meaning | Example |
|---|---|---|
. | Any character except a line break (with the s flag, line breaks too) | a.c matches abc, a1c, a-c |
\d | A digit, 0 to 9 | \d{3} matches 123 |
\D | Any character that is not a digit | \D+ matches abc, --- |
\w | A letter, digit or underscore, [A-Za-z0-9_] (ASCII only in JavaScript) | \w+ matches hello_world |
\W | Any character that is not a word character | \W matches a space, @, ! |
\s | Whitespace: space, tab, line break and other Unicode spaces | \s+ matches runs of spaces or tabs |
\S | Any character that is not whitespace | \S+ matches one word |
[abc] | Any one character in the set | [aeiou] matches a, e |
[^abc] | Any one character not in the set | [^0-9] matches a, Z |
[a-z] | A character range | [A-Fa-f0-9] matches hex digits |
Quantifiers
| Pattern | Meaning | Example |
|---|---|---|
* | 0 or more | ab*c matches ac, abc, abbc |
+ | 1 or more | ab+c matches abc, abbc (not ac) |
? | 0 or 1 (optional) | colou?r matches color, colour |
{n} | Exactly n times | \d{4} matches 2026 |
{n,m} | Between n and m times | \d{2,4} matches 12, 123, 1234 |
{n,} | n or more times | \w{3,} matches words of 3 or more characters |
*? +? ?? | Lazy: match as few characters as possible | <.+?> matches one tag at a time |
Anchors and Boundaries
| Pattern | Meaning |
|---|---|
^ | Start of the string (start of each line with the m flag) |
$ | End of the string (end of each line with the m flag) |
\b | Word boundary, the edge between a word character and anything else |
\B | Not a word boundary |
Groups, Backreferences and Lookaround
| Pattern | Meaning |
|---|---|
(abc) | Capturing group: stores the match |
(?:abc) | Non-capturing group: groups without storing |
(?<name>abc) | Named capturing group (JavaScript syntax) |
\1 | Backreference: repeats what group 1 matched |
a|b | Alternation: a or b |
(?=abc) | Positive lookahead: must be followed by abc |
(?!abc) | Negative lookahead: must not be followed by abc |
(?<=abc) | Positive lookbehind: must be preceded by abc |
(?<!abc) | Negative lookbehind: must not be preceded by abc |
JavaScript Flags
| Flag | Name | Effect |
|---|---|---|
g | Global | Find all matches, not just the first |
i | Ignore case | /hello/i matches Hello and HELLO |
m | Multiline | ^ and $ match at line boundaries |
s | Dot all | . also matches line breaks |
u | Unicode | Treats the pattern as Unicode code points and enables \p{...} property escapes |
v | Unicode sets | An upgrade to u with set operations inside character classes (cannot be combined with u) |
y | Sticky | Match only at the current position (lastIndex) |
d | Indices | Report the start and end index of each match and group |
20 Practical Regex Patterns
Each pattern shows the regex, a breakdown of its parts, and examples that were tested. Patterns with flags list them next to the regex. Patterns validate the whole string, so they are anchored with ^ and $, except the ones meant to find text inside a larger string.
1. Email Address (Practical)
^[a-zA-Z0-9._%+-]+@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$
^ Start of the string
[a-zA-Z0-9._%+-]+ Local part: letters, digits and . _ % + -
@ Literal @ symbol
(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+ One or more domain labels, each ending in a dot; a label cannot start or end with a hyphen
[a-zA-Z]{2,} Top-level domain, at least 2 letters
$ End of the string
Matches:
[email protected]
[email protected]
[email protected]
Does not match:
@missing.com
[email protected]
[email protected]
[email protected]
user@localhost
Known gaps: it accepts [email protected] (two dots in a row before the @), rejects a bare host such as user@localhost, and rejects non-ASCII letters. For a form field, <input type="email"> already applies the browser's own rule, and a confirmation email is the only real proof that an address works.
2. URL (HTTP and HTTPS)
^https?:\/\/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}(?::\d{1,5})?(?:[\/?#]\S*)?$ (flags: i)
https?:\/\/ "http://" or "https://"
(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ Host labels of up to 63 characters, no leading or trailing hyphen
[a-z]{2,63} Top-level domain, 2 to 63 letters
(?::\d{1,5})? Optional port
(?:[\/?#]\S*)? Optional path, query or fragment with no spaces
Matches:
https://example.com
https://www.example.co.uk/path?q=1&r=2#top
https://example.com:8080/a?b=1#c
https://example.technology
http://sub.domain.org
Does not match:
ftp://files.com
https://localhost:3000
http://exa mple.com
https://-bad-.com
https://example..com
A widely copied older URL pattern limits the top-level domain to 6 characters, so it rejects https://example.technology. This one allows up to 63. It does not accept localhost or raw IP addresses. To validate a URL in JavaScript, new URL(input) inside a try/catch is more reliable than any regex.
3. IPv4 Address
^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$
25[0-5] 250 to 255
2[0-4]\d 200 to 249
[01]?\d\d? 0 to 199
\. Dot separator (the first three groups repeat it)
Matches:
192.168.1.1
255.255.255.0
0.0.0.0
Does not match:
256.1.1.1
192.168.1
1.2.3.4.5
It accepts leading zeros such as 001.002.003.004, does not check for reserved ranges, and does not cover IPv6. To work out network sizes and host ranges, use our IP Subnet Calculator.
4. US Phone Number
^(\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$
(\+1[-.\s]?)? Optional country code +1
\(?\d{3}\)? Area code, parentheses optional
[-.\s]? Optional separator (dash, dot or space)
\d{3} Exchange
\d{4} Subscriber number
Matches:
(555) 123-4567
+1-555-123-4567
5551234567
555.123.4567
Does not match:
123-456
555-1234
(555) 123-45678
This is a shape check only. It accepts (555 123-4567 with an unbalanced parenthesis and does not apply the North American numbering rules (an area code cannot start with 0 or 1). Strip formatting and validate the digits if that matters.
5. Date (YYYY-MM-DD)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
\d{4} Four-digit year
(0[1-9]|1[0-2]) Month 01 to 12
(0[1-9]|[12]\d|3[01]) Day 01 to 31
Matches:
2026-01-30
2000-01-01
2026-02-31 (false positive: February has no 31st)
Does not match:
2026-13-01
2026-00-15
2026-1-1
The pattern checks the shape, not the calendar, so 2026-02-31 passes. Confirm the date is real by parsing it and comparing it back, as shown below.
function isRealDate(s) {
const d = new Date(s + 'T00:00:00Z');
return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === s;
}
isRealDate('2026-02-28') // true
isRealDate('2026-02-31') // false
6. Hex Color Code
^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$ (flags: i)
# Literal hash
[0-9a-f]{3,4} #RGB or #RGBA shorthand
[0-9a-f]{6} #RRGGBB
[0-9a-f]{8} #RRGGBBAA (with transparency)
Matches:
#FF5733
#abc
#abcd
#FF573380
Does not match:
#GG0000
FF5733
#12345
#1234567
CSS accepts 3, 4, 6 and 8 digits and ignores case, so the older three-or-six version rejects valid values such as #FF573380.
7. HTML Tag Pair (Simple)
<([a-z][a-z0-9]*)\b[^>]*>(.*?)<\/\1> (flags: gis)
<([a-z][a-z0-9]*) Opening tag name, captured as group 1
\b[^>]*> Attributes and the closing >
(.*?) Tag content, lazy match
<\/\1> Closing tag that repeats group 1
Matches:
<p>text</p>
<div class="box">content</div>
<P>text</P>
Does not match:
<p>text</div>
Do not use regex to parse real HTML. With nested tags of the same name, <div><div>a</div></div> matches only <div><div>a</div>. Use DOMParser or a server-side parser for anything beyond a quick search.
8. File Extension
\.([a-zA-Z0-9]{1,10})$
\. Literal dot
([a-zA-Z0-9]{1,10}) Extension of 1 to 10 characters, captured
$ End of the string
report.pdf captures pdf
archive.tar.gz captures gz
image.JPEG captures JPEG
noextension no match
Only the last extension is captured (archive.tar.gz gives gz), and a dotfile such as .gitignore returns gitignore. In Node.js, path.extname() handles these cases for you.
9. Password Rules (and What NIST Recommends)
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z\d\s]).{8,}$
(?=.*[a-z]) At least one lowercase letter
(?=.*[A-Z]) At least one uppercase letter
(?=.*\d) At least one digit
(?=.*[^A-Za-z\d\s]) At least one symbol (any character that is not a letter, digit or space)
.{8,} At least 8 characters in total
Matches:
Pa$$w0rd!
MyStr0ng@Pass
Correct-Horse-9
Does not match:
password
12345678
Passw0rd
Lookaheads are the standard way to require several conditions at once, so this is a good example of the technique. As a password policy it is outdated: NIST SP 800-63B-4 (July 2025) says services shall not impose composition rules such as mixing character types, shall require at least 15 characters when a password is the only factor (8 when it is one of several factors), and should allow at least 64. A length-only check that follows that guidance looks like this:
^.{15,}$ (flags: u)
Matches:
correct horse battery staple
aaaaaaaaaaaaaaa
Does not match:
short1!
fourteen chars
Pair it with a blocklist of known-breached passwords, and see our password security guide for the wider picture.
10. UUID Version 4
^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ (flags: i)
[0-9a-f]{8} 8 hex characters
4[0-9a-f]{3} Version 4 marker, then 3 hex characters
[89ab][0-9a-f]{3} Variant bits (binary 10xx), then 3 hex characters
[0-9a-f]{12} 12 hex characters
Matches:
550e8400-e29b-41d4-a716-446655440000
550E8400-E29B-41D4-A716-446655440000
Does not match:
550e8400-e29b-11d4-a716-446655440000
550e8400e29b41d4a716446655440000
The i flag matters because RFC 9562 allows uppercase, lowercase or mixed-case hex. For UUID version 7, the time-ordered format introduced in that RFC, change the 4 to a 7. Generate test values with our UUID Generator.
11. Positive Integer
^[1-9]\d*$
[1-9] First digit cannot be zero
\d* Any number of further digits
Matches:
1
42
100000
Does not match:
0
-5
3.14
007
12. Decimal Number (With Optional Minus Sign)
^-?\d+(\.\d+)?$
-? Optional minus sign
\d+ Integer part
(\.\d+)? Optional decimal part
Matches:
42
-3.14
0.5
Does not match:
.5
3.
1e5
No thousands separators or exponents. For user input, Number(text) plus Number.isFinite() is often simpler.
13. Slug (URL-Friendly String)
^[a-z0-9]+(-[a-z0-9]+)*$
[a-z0-9]+ Lowercase letters and digits
(-[a-z0-9]+)* Groups joined by single hyphens
Matches:
my-blog-post
regex101
Does not match:
My Blog Post
--double-dash
trailing-
14. Username (3 to 16 Characters)
^[a-zA-Z][a-zA-Z0-9_]{2,15}$
[a-zA-Z] Must start with a letter
[a-zA-Z0-9_]{2,15} 2 to 15 more letters, digits or underscores
Matches:
alice_42
Bob
Does not match:
1user
ab
this_is_way_too_long_username
15. Credit Card Number (Format Only)
^(?:4\d{12}(?:\d{3})?|(?:5[1-5]\d{2}|222[1-9]|22[3-9]\d|2[3-6]\d{2}|27[01]\d|2720)\d{12}|3[47]\d{13}|6(?:011|5\d{2})\d{12})$
4\d{12}(?:\d{3})? Visa: starts with 4, 13 or 16 digits
(?:5[1-5]\d{2}|222[1-9]|22[3-9]\d|2[3-6]\d{2}|27[01]\d|2720)\d{12} Mastercard: 51 to 55, or the 2-series range 2221 to 2720, 16 digits
3[47]\d{13} American Express: 34 or 37, 15 digits
6(?:011|5\d{2})\d{12} Discover: 6011 or 65, 16 digits
Matches:
4111111111111111
5500000000000004
2221000000000009
378282246310005
6011111111111117
Does not match:
4111
2721000000000000
1234567890123456
Mastercard has issued 2-series numbers (2221 to 2720) since 2017, so a pattern that only allows 51 to 55 rejects real cards. Discover also has other ranges, so treat this as a quick shape check. A pattern cannot tell whether a number is valid; run the Luhn check next, strip spaces and dashes first, and let your payment processor do the real validation. The examples are well-known test numbers, and you can make more with our Test Credit Card Generator.
function luhnOk(digits) {
let sum = 0;
for (let i = digits.length - 1, alt = false; i >= 0; i--, alt = !alt) {
let n = Number(digits[i]);
if (alt) { n *= 2; if (n > 9) n -= 9; }
sum += n;
}
return sum % 10 === 0;
}
16. Time (24-Hour Format)
^([01]\d|2[0-3]):([0-5]\d)$
([01]\d|2[0-3]) Hours 00 to 23
([0-5]\d) Minutes 00 to 59
Matches:
09:30
23:59
00:00
Does not match:
24:00
9:5
12:60
17. MAC Address (Consistent Separator)
^[0-9a-f]{2}([:-])(?:[0-9a-f]{2}\1){4}[0-9a-f]{2}$ (flags: i)
[0-9a-f]{2} First byte
([:-]) Separator, captured as group 1
(?:[0-9a-f]{2}\1){4} Four more bytes, each followed by the same separator
[0-9a-f]{2} Last byte
Matches:
00:1A:2B:3C:4D:5E
00-1A-2B-3C-4D-5E
Does not match:
00:1A-2B:3C-4D:5E
00:1A:2B:3C:4D
001A.2B3C.4D5E
The backreference \1 forces one separator throughout, which the common ([:-]) version misses. The dotted format some network gear uses (001A.2B3C.4D5E) is not covered.
18. ZIP Code (US 5-Digit or ZIP+4)
^\d{5}(-\d{4})?$
\d{5} Five digits
(-\d{4})? Optional hyphen and four more digits
Matches:
90210
90210-1234
Does not match:
9021
902101234
19. Whitespace Trimmer
^\s+|\s+$ (flags: g)
^\s+ Leading whitespace
| or
\s+$ Trailing whitespace
" hello ".replace(/^\s+|\s+$/g, "") gives "hello"
In JavaScript, " hello ".trim() does the same job, and \s also covers non-breaking and other Unicode spaces. Use the regex form in editors and tools that only offer find and replace.
20. Duplicate Words
\b(\w+)\s+\1\b (flags: gi)
(\w+) Capture a word
\s+ Followed by whitespace
\1 Followed by the same word (backreference)
Matches:
the the
is is
The the
Does not match:
the then
Some repeats are correct English ("that that", "had had"), so review matches rather than deleting them automatically.
Tips for Writing Better Regex
- Be specific. Use
[a-zA-Z]instead of.when you know you want letters. Broad patterns match things you did not intend. - Anchor validation patterns. Without
^and$,\d{3}matches insideabc123def. - Prefer lazy quantifiers for extraction.
.*?stops at the first closing delimiter, while.*runs to the last one. - Name your groups.
(?<year>\d{4})-(?<month>\d{2})is easier to read and maintain than(\d{4})-(\d{2}). - Test edge cases. Try an empty string, a very long string, and input with special characters and line breaks.
- Watch for catastrophic backtracking. Nested quantifiers such as
(a+)+$can freeze a program. On our test machine (Node.js 22),/(a+)+$/.test('a'.repeat(28) + '!')took roughly 2 to 3 seconds (yours will differ), and each extraaroughly doubled the time. OWASP lists(a+)+$,([a-zA-Z]+)*$and(a|aa)+$as classic examples.
For search and replace across many lines or files, our Find and Replace tool accepts regex, and more developer utilities are in the Developer Tools hub. If you use schedules, our cron expressions guide covers another compact syntax.
Frequently Asked Questions
What is the difference between .* and .*? (greedy vs lazy)?
.* is greedy: it matches as many characters as possible. .*? is lazy: it matches as few as possible. Given <b>hello</b> world <b>foo</b>, <b>.*</b> matches the whole string from the first <b> to the last </b>, while <b>.*?</b> matches only <b>hello</b>. Use the lazy form when you want each occurrence separately.
Should I use regex to validate email addresses?
Use a simple regex or <input type="email"> to catch obvious typos, and then send a confirmation email. The HTML Standard itself calls RFC 5322 too strict before the @, too vague after it, and too lax in other ways to be practical, and uses its own simpler pattern for type=email. No regex can prove an address exists or that the person can receive mail at it.
Why does my regex work in JavaScript but not in Python?
The engines differ in a few ways. Python's re module names groups with (?P<name>...), while JavaScript uses (?<name>...). Python requires lookbehind patterns to have a fixed width, so (?<=ab+)c fails in Python but works in JavaScript. In Python, \w and \d match Unicode letters and digits by default for str patterns, while JavaScript's \w is ASCII only. Python's $ also matches just before a trailing newline, while JavaScript's does not unless you use the m flag. Test in the language you will ship, and remember the Regex Tester here runs JavaScript.
What is catastrophic backtracking and how do I avoid it?
It happens when a pattern with nested or overlapping repetition, such as (a+)+$, has to try an exponential number of ways to fail on a non-matching input, which can freeze a program and is the basis of ReDoS attacks. Avoid nested quantifiers, make alternatives mutually exclusive, and limit input length before matching. Python 3.11 added atomic groups and possessive quantifiers to stop the engine from backtracking into a group, and JavaScript has neither, so in JavaScript restructure the pattern instead.
Sources
- MDN: Regular expressions cheatsheet
- MDN: Lookbehind assertion
- MDN: RegExp unicodeSets (the v flag)
- MDN: CSS hex-color
- Python documentation: re, regular expression operations
- HTML Standard: valid e-mail address
- NIST SP 800-63B-4: Authentication and Authenticator Management (July 2025)
- RFC 9562: Universally Unique IDentifiers (UUIDs)
- OWASP: Regular expression Denial of Service (ReDoS)
- PayJunction: MasterCard 2-Series Cards (range 222100 to 272099)