Quick answer
Match the string against a regular expression that checks the 8-4-4-4-12 hex pattern, a version digit from 1 to 8, and a variant digit of 8, 9, a or b. Add the case-insensitive flag. Then decide whether you also accept the special Nil and Max UUIDs. In JavaScript, the uuid package’s validate() does all of this for you.
The regex
Strict: RFC 9562 versions 1 to 8
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
Most of it just checks the shape: 32 hex digits in groups of 8, 4, 4, 4 and 12. Two parts do more:
[1-8]is the first digit of the third group. That digit is the version, and RFC 9562 defines versions 1 to 8.[89ab]is the first digit of the fourth group. Its top two bits are the variant, and the standard layout is binary10, which only allows hex 8, 9, a or b.
- Random
- Version, always 4
- Variant, 8, 9, a or b
The ^ and $ anchors make sure the whole string is a UUID, not just part of it. The i flag accepts uppercase.
Loose: any hex in the right shape
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
This one ignores the version and variant. Use it when you accept IDs from other systems that may not follow the standard, such as old Microsoft GUIDs with a c or d variant digit, or test data with a version of 0.
Here is what each one accepts:
| Input | Strict | Loose |
|---|---|---|
9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d |
Yes | Yes |
9B1DEB4D-3B7D-4BAD-9BDD-2B0D7B3DCB6D |
Yes | Yes |
00000000-0000-0000-0000-000000000000 (Nil) |
No | Yes |
9b1deb4d-3b7d-0bad-9bdd-2b0d7b3dcb6d (version 0) |
No | Yes |
9b1deb4d-3b7d-4bad-cbdd-2b0d7b3dcb6d (variant c) |
No | Yes |
{9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d} |
No | No |
9b1deb4d3b7d4bad9bdd2b0d7b3dcb6d |
No | No |
Nil and Max
RFC 9562 defines two special values. The Nil UUID is all zeros and the Max UUID is all ones (all f). They are valid UUIDs, but they have no version or variant digits, so the strict regex rejects them.
They are often used as placeholders, for example “no parent” or “end of range”. If your field should allow them, add them as alternatives:
/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i
If the field holds IDs of real records, it is usually better to reject them. See Nil and Max UUID for more.
In JavaScript
The npm uuid package uses the Nil and Max regex above, so it accepts any case and rejects braces or missing hyphens.
import { validate, version } from 'uuid';
validate('9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'); // true
validate('{9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d}'); // false
validate('00000000-0000-0000-0000-000000000000'); // true (Nil)
version('9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'); // 4
// Only accept v7
const isV7 = (s) => validate(s) && version(s) === 7;
version() throws a TypeError for invalid input, so call validate() first. It returns 0 for Nil and 15 for Max.
In Python
The standard library’s uuid.UUID() raises ValueError for bad input, but it is generous. It accepts curly braces, a urn:uuid: prefix, no hyphens at all, and even hyphens in odd places. It also does not check the version or variant. If you want only the standard form, compare against the normalised string:
import uuid
def parse_uuid(s: str) -> uuid.UUID | None:
try:
u = uuid.UUID(s)
except ValueError:
return None
if str(u) != s.lower():
return None # braces, urn: prefix or missing hyphens
return u
u = parse_uuid("9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d")
u.version # 4
u.variant == uuid.RFC_4122 # True for the standard layout
If you use a regex in Python instead, call fullmatch(). With re.match() and $, a string with a trailing newline still passes, because $ also matches just before a final newline.
import re
UUID_RE = re.compile(
r"[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}",
re.IGNORECASE,
)
UUID_RE.fullmatch(s) is not None
Edge cases to decide on
A regex answers “does this look like a UUID?”. These are the choices it leaves to you:
Uppercase
It is valid, and RFC 9562 says input is case-insensitive. Convert to lowercase before you store or compare IDs.
Spaces and newlines
Both regexes reject them. Trim input from forms and files first if you want to be friendly.
Braces and urn:uuid:
These are common wrappers, not part of the UUID. Strip them before validating if your users paste IDs from Windows tools. See GUID vs UUID.
Your database may be looser
PostgreSQL’s uuid type accepts uppercase, braces and missing hyphens, and raises an error for anything else. Validate before you query so bad input becomes a clean 400 or 404.
Finding UUIDs in longer text
Remove the ^ and $, put \b at both ends, and add the g flag.
A valid UUID only means the format is right. It does not mean the record exists, or that the person asking is allowed to see it. To check a single ID by hand, paste it into the UUID validator.

