How-To regex regular expressions developer reference

Regex Cheat Sheet: Syntax, Flags and 20 Ready-to-Use Patterns

By ClickSolveTools Editorial TeamUpdated 10 min read

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

PatternMeaningExample
.Any character except a line break (with the s flag, line breaks too)a.c matches abc, a1c, a-c
\dA digit, 0 to 9\d{3} matches 123
\DAny character that is not a digit\D+ matches abc, ---
\wA letter, digit or underscore, [A-Za-z0-9_] (ASCII only in JavaScript)\w+ matches hello_world
\WAny character that is not a word character\W matches a space, @, !
\sWhitespace: space, tab, line break and other Unicode spaces\s+ matches runs of spaces or tabs
\SAny 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

PatternMeaningExample
*0 or moreab*c matches ac, abc, abbc
+1 or moreab+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

PatternMeaning
^Start of the string (start of each line with the m flag)
$End of the string (end of each line with the m flag)
\bWord boundary, the edge between a word character and anything else
\BNot a word boundary

Groups, Backreferences and Lookaround

PatternMeaning
(abc)Capturing group: stores the match
(?:abc)Non-capturing group: groups without storing
(?<name>abc)Named capturing group (JavaScript syntax)
\1Backreference: repeats what group 1 matched
a|bAlternation: 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

FlagNameEffect
gGlobalFind all matches, not just the first
iIgnore case/hello/i matches Hello and HELLO
mMultiline^ and $ match at line boundaries
sDot all. also matches line breaks
uUnicodeTreats the pattern as Unicode code points and enables \p{...} property escapes
vUnicode setsAn upgrade to u with set operations inside character classes (cannot be combined with u)
yStickyMatch only at the current position (lastIndex)
dIndicesReport 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:
  user@example.com
  first.last+tag@sub.domain.org
  user@sub-domain.example.co.uk
Does not match:
  @missing.com
  user@.com
  user@a..com
  user@-a.com
  user@localhost

Known gaps: it accepts a..b@x.com (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

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