UUID versions (and when v4 is enough)
Published: 2026-06-26
How RFC 4122 UUIDs encode version and variant bits, what versions 1–5 mean in practice, and why random version 4 IDs are the right default for most applications.
A UUID (Universally Unique Identifier) is a 128-bit value, usually written as 32 hexadecimal digits in five groups separated by hyphens: 8-4-4-4-12. The standard most developers mean is RFC 4122, which defines several versions—different recipes for filling those 128 bits. In modern apps, version 4 (random) is the default choice for primary keys, request IDs, and file names. Understanding the version nibble and variant bits helps you read logs, debug integrations, and know when a different version—or a non-UUID scheme like ULID or NanoID—is a better fit.
UUID layout in one minute
A canonical string looks like:
550e8400-e29b-41d4-a716-446655440000
|------| |---| |---| |---| |----------|
time time ver clk node (example labels — meaning depends on version)
Under the hood it is 16 bytes (128 bits). RFC 4122 packs metadata into fixed positions:
| Field | Position in string | Role |
|---|---|---|
| Version | First hex digit of the third group (…-XXXX-…) |
Tells you how the UUID was generated (1–5) |
| Variant | First hex digit of the fourth group | Must follow the RFC 4122 variant (8, 9, a, or b in that nibble) |
For version 4, the version nibble is 4 (for example …-4xxx-…), and the variant nibble is 8, 9, a, or b (for example …-8xxx-…). The remaining bits are random (from a cryptographically secure source in production).
Example v4 (random):
f47ac10b-58cc-4372-a567-0e02b2c3d479
^ ^
version 4 RFC 4122 variant (a = 1010 binary)
Letter case does not change the value. F47AC10B-… and f47ac10b-… are the same UUID. Databases and APIs often normalize to lowercase; comparisons should be case-insensitive.
UUID versions at a glance
RFC 4122 defines five generation algorithms. You can read the version from the string without decoding the rest.
| Version | How bits are filled | Typical use |
|---|---|---|
| 1 | Timestamp + clock sequence + MAC address (or random node if no MAC) | Legacy systems; time-ordered if from one host |
| 2 | DCE security (like v1 + POSIX UID/GID) | Rare today; domain-specific |
| 3 | Name-based, MD5 hash of namespace + name | Deterministic ID from a fixed name (deprecated hash) |
| 4 | Random (122 random bits + version/variant) | Default for new IDs |
| 5 | Name-based, SHA-1 hash of namespace + name | Deterministic ID with a stronger hash than v3 |
Nil UUID: 00000000-0000-0000-0000-000000000000 — all zeros. It is valid syntax and often means “no id” or a null placeholder in tests and APIs.
Version 1: time + hardware flavor
Version 1 embeds a timestamp and often a network interface identifier. That can be useful when you want rough time ordering without a separate column, but it also leaks when the ID was created and sometimes where (MAC-derived node bits). Many teams avoid v1 for public-facing IDs for privacy and portability reasons.
Versions 3 and 5: same name, same UUID
Given a namespace UUID and a name string, v3/v5 always produce the same UUID. That is ideal for idempotent imports (“user [email protected] in namespace X always gets UUID Y”) but unsuitable when you need unpredictability. Prefer v5 over v3 if you must use name-based UUIDs—MD5 is legacy for new designs.
Version 4: random is the default
Version 4 sets version and variant bits, then fills the rest with random data. Collision risk is negligible at application scale: with proper randomness, birthday-paradox math only becomes relevant at volumes far beyond typical web apps.
When v4 is enough (most cases):
- Database primary keys and foreign keys
- Correlation / request IDs in logs and traces
- Upload filenames and storage keys where sort order does not matter
- Session or resource tokens where opacity matters more than embeddable metadata
You do not need v1/v3/v5 unless you explicitly want timestamp ordering or deterministic name hashing.
When to consider something other than v4
| Need | Better option |
|---|---|
| Sortable by creation time (lexicographic) | ULID (timestamp prefix + randomness) or DB created_at + v4 |
| Shorter, URL-friendly IDs | NanoID or similar |
| Deterministic ID from stable name | UUID v5 (namespace + name) |
| Standard interop with existing v1 data | UUID v1 (rare for greenfield) |
| Integer keys for human admin UIs | Auto-increment or snowflake-style IDs — not UUIDs |
UUIDs are not secrets. A v4 hides no payload, but guessing valid IDs can still be a concern for authorization—use proper access checks, not “obscure UUID.”
Randomness: what “good” looks like
Production v4 UUIDs should use a cryptographically secure random source:
- Browsers:
crypto.randomUUID()orcrypto.getRandomValues() - Node.js:
crypto.randomUUID()/randomUUIDfromnode:crypto - Avoid
Math.random()for identifiers
The UUID Generator uses crypto.randomUUID() when the browser provides it, otherwise getRandomValues with correct version/variant bit masking—matching RFC 4122 v4 layout.
Validating and storing UUIDs
Common rules:
- Format: 36 characters with hyphens at fixed positions, or 32 hex digits without hyphens in some databases.
- Type: Store as UUID/native type when your database supports it; otherwise char(36) with a consistent case convention.
- Compare case-insensitively unless a broken client forces otherwise.
- Do not truncate for display in security-sensitive flows (short prefixes collide more often).
Parsing libraries usually accept uppercase and lowercase hex; invalid version/variant combinations should be rejected if you validate strictly.
Common pitfalls
- Assuming UUIDs are ordered: v4 strings are not time-sorted. Use a timestamp column or ULIDs if sort order matters in indexes or UI.
- Using v3 for new work: Prefer v5 (SHA-1) for name-based IDs; v3 remains only for legacy compatibility.
- Treating UUID as secret: IDs are identifiers, not authentication. Pair them with real auth and authorization.
- Collisions across systems: v4 uniqueness is statistical, not coordinated. Merging datasets rarely collides; if you need global coordination, use a central allocator or deterministic v5 with agreed namespaces.
- Mixing formats in one column: Pick hyphenated or compact hex consistently in APIs and logs.
Nil UUID in tests and APIs
00000000-0000-0000-0000-000000000000 is the nil UUID. Frameworks use it for “empty” optional IDs, GraphQL nullability patterns, and test fixtures. It is not random—never use it as a production primary key for real entities.
Try it locally in your browser
Use the UUID Generator to:
- Generate RFC 4122 version 4 UUIDs with Web Crypto randomness.
- Insert the nil UUID for fixtures and null-id scenarios.
- Batch up to 500 IDs (one per line) with lowercase or uppercase hex.
- Copy results in one click—processing stays in your browser; nothing is uploaded.
For sortable identifiers, try the ULID Generator. For shorter custom alphabets, use the NanoID Generator.
Related reading
- Hex encoding: UTF-8 bytes explained — UUIDs are often shown as hex groupings; the same byte-view mindset applies when debugging binary payloads.