Guide
Regex Cheat Sheet: Common Patterns Every Developer Needs
Twenty-five patterns you'll actually use, the greedy/lazy distinction that owns half of regex bugs, and where JavaScript regex differs from PCRE.
By Buğra SözeriPublished
A working regex isn’t a one-liner you memorise; it’s a one-liner plus the assumptions about your input and your flavour. This cheat sheet pairs each pattern with what it actually matches, the edge cases it misses, and which regex engines it works in. Drop any of them into our live regex tester to see matches and captures interactively.
Notation used below
Patterns are shown without the surrounding /.../ delimiters except where flags matter. Assume ^ and $are anchored to a single value (the whole field), not multi-line. When a pattern uses features that aren’t universal, the flavour note spells out which engines it works in. Where there’s both a permissive and strict form, both are given.
Identifiers and names
Email — permissive
^[^\s@]+@[^\s@]+\.[^\s@]+$Three character classes separated by @ and ., none of which contain whitespace or another@. It accepts almost every real-world email and rejects the most obvious typos. It will not validate against RFC 5322 — quoted local parts and IP-literal domains slip through or are rejected depending on which corner.
Email — HTML5 form spec
^[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])?)*$The pattern WHATWG specifies for HTML input type=“email“. Tighter than the permissive form but still doesn’t claim full RFC 5322 coverage — the spec explicitly says “wilfully non-compliant.” Use this when you want to match browser behaviour.
URL — HTTPS only
^https://[^\s/$.?#].[^\s]*$Permissive HTTPS URL. Rejects obvious garbage but accepts anything past the host that’s non-whitespace. For real validation, defer to new URL() in the host language; regex is for filtering inputs, not parsing them.
Slug (URL-safe identifier)
^[a-z0-9]+(?:-[a-z0-9]+)*$Lowercase letters and digits with single hyphens between groups. No leading or trailing hyphens, no double hyphens. This is the slug shape every CMS uses.
Username — alnum and underscore, 3-20 chars
^[A-Za-z0-9_]{3,20}$UUID (any version)
^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$UUID v4 (strict)
^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$Enforces the 4 in position 13 and the variant bits in position 17 (one of 8, 9, a, b). Use this when you care about version, not just shape.
Numbers, money, codes
Integer (signed)
^-?\d+$Decimal number (signed, optional fractional part)
^-?\d+(?:\.\d+)?$Currency amount (US-style)
^\$?\d{1,3}(?:,\d{3})*(?:\.\d{2})?$Optional dollar sign, thousands separators every three digits, optional two-decimal cents. Doesn’t handle negatives; for accounting use, prepend ^-? or wrap in parens.
Hex color (3, 4, 6, or 8 digits)
^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$The 4 and 8-digit forms include an alpha channel (#RRGGBBAA) supported by CSS Color Level 4 and every modern browser.
Semantic version (semver 2.0.0)
^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$This is the canonical regex published on semver.org. It captures major, minor, patch, optional pre-release, and optional build metadata. It correctly rejects leading zeros in numeric components.
Dates and times
ISO 8601 date (YYYY-MM-DD)
^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$Restricts the month to 01-12 and the day to 01-31. Does not validate that the day exists in the given month (Feb 30 passes). For real date validation, parse with the host language and check for errors.
ISO 8601 timestamp with timezone
^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$Accepts Z for UTC or a numeric offset like +02:00. Allows optional fractional seconds.
HH:MM 24-hour time
^(?:[01]\d|2[0-3]):[0-5]\d$Network addresses
IPv4
^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$Octet range strictly 0-255. Rejects leading zeros like 192.168.001.001 (which is unambiguous to humans but some systems interpret as octal, so the rejection is defensible).
IPv6 — practical
^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$Matches the canonical eight-group form. It will not match IPv6 with :: compression or with embedded IPv4 like ::ffff:192.0.2.1. The full RFC 4291 regex is over a hundred characters; for production use, defer to your language’s inet-pton equivalent.
MAC address
^(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}$Port number
^(?:6553[0-5]|655[0-2]\d|65[0-4]\d{2}|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$0-65535, strictly bounded.
Phone numbers
US phone — permissive
^(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$Optional +1, optional parens around area code, optional separators of hyphen, period, or space. Accepts the common US formats; rejects non-US numbers.
International phone (E.164)
^\+[1-9]\d{1,14}$E.164 is the ITU spec: leading +, country code starting non-zero, total length 2-15 digits including the country code. No spaces, no parens. This is the format every SMS gateway and most CRMs expect.
Strings and structure
Whitespace-only string
^\s*$Strip leading/trailing whitespace (replace with empty)
^\s+|\s+$Collapse multiple spaces to one (replace with single space)
\s{2,}Match content between brackets (lazy)
\[([^\]]*)\]Captures characters that aren’t a closing bracket — the negated character class is the cleanest way to do non-greedy “everything until the next delimiter.”
Quantifier behaviour
Greedy (the default)
*, +, ?, and {n,m} match as much as possible by default, then back off to find a match for the rest of the pattern. On <b>hi</b><b>bye</b>, the pattern <b>.*</b> matches the entire string, not the first tag pair.
Lazy (suffix ?)
Add a ? to make a quantifier match as little as possible. <b>.*?</b> matches <b>hi</b> only, which is almost always what you wanted.
Possessive and atomic groups
Possessive quantifiers (*+, ++) and atomic groups ((?>...)) refuse to backtrack once matched. They eliminate catastrophic backtracking in pathological inputs. PCRE, Java, and Ruby support them; JavaScript does not (use a workaround like a lookahead with a back-reference).
Lookahead and lookbehind
Lookarounds assert a condition without consuming characters. They’re zero-width.
- Positive lookahead
(?=...)— “followed by.”\d+(?=px)matches digits that are followed bypxwithout including thepxin the match. - Negative lookahead
(?!...)— “not followed by.”foo(?!bar)matchesfoonot followed bybar. - Positive lookbehind
(?<=...)— “preceded by.” - Negative lookbehind
(?<!...)— “not preceded by.”
JavaScript supports lookahead in all versions, lookbehind only from ES2018 onwards. Many older engines (and the V8 version in some bundled Node builds) limit lookbehinds to fixed-width patterns; PCRE and Python’s regex module allow variable-width.
Flavour differences worth remembering
- JavaScript: no possessive quantifiers, no atomic groups, lookbehind from ES2018. The
g(global) flag makesString.matchreturn all matches butString.replacebehave differently — the most-bugged corner of JS regex. - PCRE (Perl Compatible): the feature superset. Used by PHP, nginx, many command-line tools. Recursive patterns, conditionals, possessive quantifiers, atomic groups, named captures — all supported.
- Python: the standard
remodule is close to PCRE minus a few features. The third-partyregexmodule adds the rest plus variable-width lookbehinds and atomic groups. - Go (RE2): deliberately linear-time. No backreferences, no lookaround. Slower on simple patterns, immune to catastrophic backtracking. Rust’s
regexcrate is the same family. - .NET: similar to PCRE, plus some unique features like balancing groups (which let you actually match nested brackets — in a way every other flavour can’t).
One flag worth singling out across flavours: Unicode property escapes (\p{Letter}, \p{Number}, \p{Script=Greek}) match by codepoint category instead of by raw byte. JavaScript needs the u flag, PCRE needs (*UCP) or the /u modifier, Python supports them in the third-party regex module. Without them, \w means only ASCII [A-Za-z0-9_] and your “match any name” pattern silently rejects half the world.
The patterns to never write
- HTML parsing. Use a real parser (DOMParser in the browser, BeautifulSoup in Python, go-html in Go). Regex can extract a single attribute from a well-known shape, nothing more.
- JSON parsing. Every language has
JSON.parse. Use it. - Programming language parsing. Use a parser combinator or a real grammar.
- Anything that needs to match nested delimiters.Classic regular languages can’t count. Use a parser.
The honest takeaway
Regex is a sharp tool with a small, well-understood domain: fixed-shape strings, simple input filtering, search-and-replace in text editors. The patterns in this cheat sheet cover the 80% of cases that come up in everyday code. For the 20% that looks regex-shaped but isn’t — nested structures, natural-language matching, semantic validation — reach for a parser instead.
And when you do write a regex, paste it into our testerwith three representative inputs and three pathological inputs before you commit it. The pattern that works on your one test case is the pattern that takes production down on someone else’s.
Frequently asked questions
- Why does my email regex reject valid addresses?
- Because the RFC 5322 grammar for valid email addresses is over a page long and includes constructs like quoted local parts ("user name"@example.com) and IP-literal domains (user@[192.168.0.1]). Most 'email regexes' enforce a tiny common subset. For real validation, send a confirmation email — the regex can only filter obvious typos.
- Is regex flavour really that different across languages?
- Yes. JavaScript supports lookahead but only got lookbehind in 2018 (ES2018) and named groups in the same release. Python uses re for basic regex and regex for advanced features like recursive patterns. PCRE has the most features and is the closest to a 'regex superset'. .NET has its own quirks. Always test in the actual runtime, not regex101 with the wrong flavour selected.
- Greedy vs lazy quantifiers — which do I want?
- Greedy (the default) matches as much as possible; lazy (suffix ?) matches as little as possible. For .* inside HTML tags, you almost always want lazy: .*? — otherwise <b>hi</b><b>bye</b> matches everything from the first <b> to the last </b>. For numeric parsing where the field length is known, greedy is usually fine.
- When should I not use regex?
- Parsing HTML, JSON, YAML, programming languages, or anything with nested structure. Regex is a tool for regular languages; the moment you need to count or recurse (matching nested brackets, matching open/close tags), use a real parser. The 'parse HTML with regex' Stack Overflow answer became a cult classic because the advice is correct.
- What's the safest way to validate a URL?
- Pass it to the URL constructor in the actual language — JavaScript's new URL(input), Python's urllib.parse.urlparse, Java's java.net.URI. These implement the relevant RFCs and handle edge cases (international domains, port ranges, scheme validation) that a regex won't. Use regex only to filter for syntactic plausibility before that call.
- Why is my regex so slow on certain inputs?
- Catastrophic backtracking. Patterns with nested or overlapping quantifiers — like (a+)+ or (.*).* — can take exponential time on certain inputs. The Cloudflare global outage in July 2019 was a regex doing exactly this. Mitigations: avoid nested quantifiers, use atomic groups (?>...) or possessive quantifiers in flavours that support them, or use a linear-time engine like Rust's regex or Google's RE2.
Sources & references
Authoritative references cited by this piece. Verified by Buğra Sözeri on the dates shown and re-checked at every deploy.
- RFC 5322 — Internet Message Format — Authoritative source for the email-address grammar referenced in the email-pattern section(as of )
- RFC 3986 — Uniform Resource Identifier (URI): Generic Syntax — Reference for URL component validity(as of )
- RFC 4122 — A Universally Unique IDentifier (UUID) URN Namespace — Reference for UUID format used in the UUID pattern(as of )
- Semantic Versioning 2.0.0 — Includes the official BNF used to derive the semver regex below(as of )
- MDN — Regular expressions — Reference for JavaScript regex flavour notes(as of )
Related
- Regex tester (live)Paste a pattern and a sample; see matches, captures, and explanations in real time
- URL encode / decodeCompanion tool for the URL-pattern section
- UUID generatorGenerate v4 / v7 UUIDs that match the patterns below
- Timestamp converterUseful when the regex is the easy half of a date-parsing task
Published May 31, 2026