How to Validate a UUID with Regex, Plus Why You Might Not Want To

By Sheng Pang · Published · 3 min read

You have a string from a form field or a URL parameter and you want to know whether it is a real UUID before you send it to the database. A regular expression is the usual first reach. Here is one that is actually correct, the mistakes that make most of the ones on the internet wrong, and the built in checks that are often better.

The regex

^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$

Use it with the case insensitive flag so that uppercase input passes. Piece by piece:

  • [0-9a-f]{8}, then a hyphen: the first group is eight hex digits.
  • [0-9a-f]{4}: the second group is four hex digits.
  • [1-8][0-9a-f]{3}: the third group starts with the version digit, which must be 1 through 8 for a standard UUID.
  • [89ab][0-9a-f]{3}: the fourth group starts with the variant, which for RFC UUIDs is 8, 9, a or b.
  • [0-9a-f]{12}: the last group is twelve hex digits.
  • ^ and $ anchor the whole string so that "abc" plus a UUID plus "xyz" does not match.

Common mistakes

  • Forgetting the anchors. Without ^ and $ the pattern matches a UUID anywhere inside a longer string.
  • Allowing any hex digit for version and variant. A pattern of five plain hex groups accepts strings that are not valid UUIDs. Whether you care depends on what you do next.
  • Hard coding version 4. Many copied patterns use 4[0-9a-f]{3} for the third group. That rejects every v1 and v7 UUID. Only do this if you truly require v4.
  • Using \w or . instead of hex classes. Both accept characters that cannot appear in a UUID.

Variants you may need

Only accept version 7:

^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$

Accept the nil UUID too, since it fails the version check above:

^(?:[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)$

Accept optional braces in the Windows GUID style:

^\{?[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\}?$

That last one also accepts a string with a brace on only one side. If that matters, write two alternatives instead.

Ready to use snippets

JavaScript:

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const isUuid = (s) => UUID_RE.test(s);

Python:

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.I)
def is_uuid(s): return bool(UUID_RE.match(s))

The better option: parse it

Most languages have a UUID parser, and a parser is stricter and clearer than a regex:

# Python
import uuid
try:
    uuid.UUID(s)
except ValueError:
    ...  # not a UUID

// JavaScript with the uuid package
import { validate, version } from "uuid";
validate(s);   // true or false
version(s);    // 4, 7, ...

// Java
UUID.fromString(s);   // throws IllegalArgumentException

// C#
Guid.TryParse(s, out var g);

Be aware that Python's uuid.UUID() is lenient. It accepts braces, the urn:uuid: prefix and even a string with the hyphens removed. If you need the exact 36 character form, add a length check or use the regex.

Validate at the edge, trust inside

Check UUIDs once, where untrusted input enters: request parameters, form fields, file imports. After that, pass them around as a typed UUID value rather than a string so you never need to check again. Databases with a native uuid column will also reject malformed values for you.

Need sample values to test your validator? Grab a few from our UUID generator, then try breaking them by deleting a character.

← Back to all articles