Matching UUIDs with Regular Expressions
UUIDs (Universally Unique Identifiers) are 128-bit values, written as 32 hex digits grouped 8-4-4-4-12 with hyphens. They're used everywhere — as primary keys in distributed databases, as session tokens, as file names, as event IDs in analytics streams. But not every "UUID-shaped" string is a valid UUID: the version and variant digits carry meaning, and different UUID versions have different structural rules. Below are the patterns you need for validation, extraction, and version-specific matching.
The Core Patterns
Generic 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}$/
// Also OK with i flag:
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iThis is the correct default. It matches every UUID version (v1, v3, v4, v5, v6, v7, v8) and the nil UUID (all zeros). Use it unless you have a specific reason to require one version.
UUID v4 strict (random)
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
// ^ ^^^^
// version variant
// Version digit (position 13) must be '4'
// Variant digit (position 17) must be 8, 9, a, or bUUID v4 is the most common version — Node.js crypto.randomUUID(), Python uuid.uuid4(), Java UUID.randomUUID() all produce v4. If your entire system generates only v4, use this strict pattern.
UUID v1 (time-based)
/^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
// Version = 1. v1 embeds a MAC-based node ID and timestamp — mostly legacy.UUID without hyphens (32 hex)
/^[0-9a-fA-F]{32}$/ // Compact form used by many APIs
/^[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}$/
// Accepts both hyphenated and unhyphenated formsMicrosoft GUID (with braces)
/^\{?[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}\}?$/
// Accepts both {550e8400-e29b-41d4-a716-446655440000} and 550e8400-e29b-41d4-a716-446655440000Exclude the nil UUID
/^(?!00000000-0000-0000-0000-000000000000)[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}$/
// The negative lookahead ensures the nil UUID is rejected as a "real" UUIDLanguage-Specific Usage
JavaScript
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function isUuid(str) { return UUID.test(str); }
function isUuidV4(str) { return UUID_V4.test(str); }
isUuid('550e8400-e29b-41d4-a716-446655440000'); // true
isUuidV4('550e8400-e29b-41d4-a716-446655440000'); // true
isUuidV4('6ba7b810-9dad-11d1-80b4-00c04fd430c8'); // false (v1)
// Extract every UUID from a log line
const line = 'user 550e8400-e29b-41d4-a716-446655440000 request ba7b810-9dad-11d1-80b4-00c04fd430c8';
line.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi);Python
import re
import uuid
UUID = re.compile(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', re.I)
def is_uuid(s: str) -> bool:
return bool(UUID.match(s))
is_uuid('550e8400-e29b-41d4-a716-446655440000') # True
# Python has a built-in UUID validator that's stricter than regex:
try:
uuid.UUID('550e8400-e29b-41d4-a716-446655440000')
valid = True
except ValueError:
valid = False
# Use uuid.UUID(...) when you want to VALIDATE, use regex when you want to EXTRACT.PHP
function isUuid(string $s): bool {
return (bool) preg_match(
'/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i',
$s
);
}
function isUuidV4(string $s): bool {
return (bool) preg_match(
'/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
$s
);
}Common Pitfalls
The nil UUID passes generic validation
00000000-0000-0000-0000-000000000000is a syntactically valid UUID. If your business logic treats the nil UUID as "not set", you need to reject it explicitly with a negative lookahead — or check for it separately after regex validation.
Version-specific regex may reject legit UUIDs
A strict UUID v4 regex will reject UUIDs generated by v1, v3, v5, v6, v7, or v8 tools. If your system might one day accept UUIDs from external sources, use the generic pattern and validate version separately if needed.
Case sensitivity
UUIDs are usually written in lowercase, but uppercase is equally valid. Always include [A-Fa-f] or use the i flag.
UUID v7 is now common (2024+)
UUID v7 uses a Unix-timestamp prefix, so entries are naturally sortable — great for database primary keys. If your app relies on v4-only validation, upgrading to a v7-generating library will silently start rejecting all new IDs. Use the generic pattern to future-proof.
Not every 8-4-4-4-12 string is a real UUID
The regex validates the FORMAT. It says nothing about whether the value came from a proper crypto-secure RNG. ffffffff-ffff-ffff-ffff-ffffffffffff passes generic regex but is obviously not a real random UUID.
UUID Cheatsheet
| Goal | Pattern | Matches |
|---|---|---|
| Any UUID | /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i | v1-v8 + nil |
| UUID v4 only | /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i | random UUIDs |
| No hyphens | /^[0-9a-f]{32}$/i | compact form |
| Braced GUID | /^\{?[0-9a-f]{8}-...{12}\}?$/i | Microsoft style |
| Non-nil UUID | /^(?!00000000...)...{12}$/i | rejects all-zeros |
Testing Your UUID Regex
Use the live Regex Tester above with these test strings:
- Match (v4):
550e8400-e29b-41d4-a716-446655440000 - Match (v1):
6ba7b810-9dad-11d1-80b4-00c04fd430c8 - Match (nil — generic regex):
00000000-0000-0000-0000-000000000000 - Match (Microsoft):
{550e8400-e29b-41d4-a716-446655440000} - Reject:
550e8400-e29b-41d4-a716-44665544000(11 digits),xyz - Edge:
550e8400e29b41d4a716446655440000— passes only unhyphenated pattern
Common Mistakes When Writing UUID Regex
A few recurring pitfalls trip up developers writing UUID validators:
- Assuming everyone uses v4. New services use v7 for sortable IDs. Old services might use v1. Only lock to v4 if you control every generator in your stack.
- Forgetting to reject the nil UUID.Generic regex accepts it. If nil means "not set" in your data model, reject it with a negative lookahead or a follow-up check.
- Case-sensitive character class.
[a-f0-9]rejects uppercase hex. Add theiflag or use[a-fA-F0-9]. - Missing the hyphens. Users often paste UUIDs into forms without hyphens. If your regex requires them, add a normalisation step first: strip non-hex, then re-insert hyphens at positions 8, 12, 16, 20.
- Confusing UUID with ULID/NanoID. ULIDs are 26 characters in Crockford base32. NanoIDs are 21 characters in a URL-safe alphabet. Neither matches UUID regex — check the actual format your system uses before writing the validator.
Performance Notes
UUID regexes are extremely fast because every quantifier is bounded ({4}, {8}, {12}). No catastrophic backtracking is possible. Validating millions of UUIDs takes tens of milliseconds. For extremely hot paths (log-line extraction from GB-sized files), a bit-level check on the version and variant nibbles can be faster than regex — but you only need that at Google-scale volumes. For 99% of apps, the regex is more than fast enough.