The UUID regex you can copy
The canonical form is eight, four, four, four, and twelve hexadecimal digits, separated by hyphens:
^[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}$
This is the safest general-purpose check: it accepts uppercase and lowercase hex, enforces the five segment lengths, and requires exactly four hyphens in the right places. With the i flag you could drop the A-F part, but spelling it out keeps the pattern portable across engines that ignore flags.
What each segment means
[0-9a-fA-F]{8}— the first 32 bits, usually the low or high timestamp depending on version.{4}— the next 16 bits, historically the version-related time field.{4}— the version nibble sits at the start of this segment (see below).{4}— the variant nibble sits at the start of this segment.{12}— the final 48 bits, typically the node (MAC or random).
The hyphens are not decorative. They are part of the RFC 4122 / 9562 textual representation, and their positions encode where each field boundary falls.
Version and variant bits, the part regex can pin
A UUID is not just thirty-two hex chars; two specific positions carry meaning:
- The version is the first hex digit of the third group (character 14, zero-indexed). Legal values today are 1 through 8: v1 time-based, v2 DCE, v3 and v5 name-based (MD5/SHA-1), v4 random, v6/v7 time-ordered, v8 custom.
- The variant is the first hex digit of the fourth group (character 19). For the RFC 4122 layout it must be 8, 9, a, or b — that is, the two most significant bits are
10.
If you only accept v4, pin those positions:
^[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}$
The 4 after the second hyphen forces version 4; [89abAB] after the third forces a valid RFC variant.
Why regex only checks the shape
This is the core argument. A regex confirms that a string looks like a UUID. It cannot confirm that the version semantics are honored. A string can have a 4 in the version slot and still be a v4 only by accident, with zero bits of randomness — perfectly "valid" to the regex and cryptographically worthless. Conversely, a future version 9 would be rejected by today's version-pinned pattern even though it is a legitimate UUID. Regex sees digits; it does not see meaning.
Common mistakes
- Dropping the case: writing
[0-9a-f]only rejects uppercase, so550E8400-...fails. Either addA-For normalize case first. - Wrong segment counts: forgetting a group or using
{16}in one block yields a pattern that matches the wrong length and silently passes garbage. - Using
\w:\wmatches digits, letters, and the underscore, so____-____-____-____-________would pass. Always use an explicit hex class. - Missing anchors: without
^and$,1234-...embedded inside a longer string would match a substring and fool a validator.
Real examples: accepted and rejected
| Input | Result | Why |
|---|---|---|
| 550e8400-e29b-41d4-a716-446655440000 | Accepted | Correct 8-4-4-4-12 shape, valid v4 |
| 550E8400-E29B-41D4-A716-446655440000 | Accepted | Uppercase is valid hex too |
| 123e4567-e89b-12d3-a456-426614174000 | Accepted | Valid v1 shape |
| not-a-uuid | Rejected | Not hexadecimal, no structure |
| 12345678-1234-1234-1234-1234567890 | Rejected | Last group has 10 digits, needs 12 |
Braces, URN prefixes, and the nil UUID
Real input is messier than the canonical form:
- Microsoft tooling wraps UUIDs in braces:
{550e8400-e29b-41d4-a716-446655440000}. Strip the braces before testing, or extend the pattern. - URN form carries a prefix:
urn:uuid:550e8400-e29b-41d4-a716-446655440000. Again, strip or allow the prefix. - The nil UUID
00000000-0000-0000-0000-000000000000is syntactically valid and means "no UUID". Treat it as a sentinel, not as a real identifier.
What to use instead
For actual validation, parse rather than match. Most languages ship a UUID type that rejects malformed input and even checks version consistency:
import uuid
try:
u = uuid.UUID("550e8400-e29b-41d4-a716-446655440000")
print(u.version) # 4
except ValueError:
print("not a uuid")
In JavaScript, crypto.randomUUID() produces v4 strings and new UUID(...) (or a small library) parses them. Use the parser: it understands nil, version, and variant far better than any regex you will write.
Generating versus validating
Keep the two jobs separate. Generating a UUID should draw from a cryptographically secure random source — most standard libraries already do — while validating one is what this page is about. A regex is the weakest possible validator and a parser is the strongest. Never hand-roll random hex and hope the version bit lands correctly; call the library generator so the version and variant nibbles are set for you. If you only need a database primary key, take the library's string, run it through the parser once on the way in, and move on. No regex in the world is more trustworthy than that round trip.