UUID Versions Explained: v1, v3, v4, v5, v6, and v7

28 February, 2026 Backend

UUID stands for Universally Unique Identifier - a 128-bit value standardised in RFC 4122 (2005) and updated in RFC 9562 (2024). The format is well-known: eight-four-four-four-twelve hexadecimal digits separated by hyphens, for example 550e8400-e29b-41d4-a716-446655440000. What is less known is that there are seven distinct versions, each with different properties, trade-offs, and appropriate use cases. Picking the wrong version causes real problems: v4 in database primary keys leads to index fragmentation; v1 leaks your server's MAC address; v3 uses a broken hash function.

The UUID Standard

RFC 4122 defines the UUID structure as 128 bits organised into five fields:

xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

Where:

  • M = version nibble (1-7)
  • N = variant bits (8, 9, a, or b for RFC 4122 UUIDs)

The variant bits in the N position distinguish RFC 4122 UUIDs from other UUID namespaces (Microsoft GUIDs use a different variant). RFC 9562 (April 2024) officially added v6 and v7, which had been widely implemented but lacked formal standardization.


v1: Time + MAC Address

UUID v1 encodes the current time as a 60-bit count of 100-nanosecond intervals since October 15, 1582 (the Gregorian calendar reform date). The remaining bits include a clock sequence (to handle clock resets) and the MAC address of the generating network interface.

Binary structure:

time_low (32 bits) - time_mid (16 bits) - version + time_hi (16 bits)
clock_seq_hi + variant (8 bits) - clock_seq_low (8 bits) - node/MAC (48 bits)

The time bits are split and reordered across the UUID fields, which means v1 UUIDs are NOT lexicographically sortable even though they encode time.

Privacy concern: The MAC address in the node field identifies the generating machine. Pre-RFC 9562 generators using real MAC addresses could be used to trace UUIDs back to specific servers - a documented issue in early Netscape Navigator, which embedded MAC addresses in cookies via v1 UUIDs. Modern implementations substitute a random 48-bit value for the node field (RFC 4122, section 4.5).

Use case: Legacy systems that need timestamp extraction without a database query. Rarely the right choice for new systems.


v2: DCE Security

UUID v2 replaces the low 32 bits of the timestamp with a POSIX UID/GID, and encodes the local domain (person=0, group=1, org=2). This creates a UUID that embeds both a timestamp and a local user identifier.

In practice, v2 is nearly unused. The specification is underspecified in RFC 4122 (described as "informational only"), the embedded POSIX IDs are rarely useful outside DCE (Distributed Computing Environment) systems, and no mainstream UUID library generates v2 by default.


v3: MD5 Name-Based

UUID v3 generates a deterministic UUID from a namespace UUID and a name string. The algorithm:

  1. Concatenate the namespace UUID (as bytes) with the name (as UTF-8 bytes)
  2. Compute MD5 hash of the concatenation
  3. Set version bits to 3 and variant bits per RFC 4122

Standard namespaces defined in RFC 4122:

  • 6ba7b810-9dad-11d1-80b4-00c04fd430c8 - DNS names
  • 6ba7b811-9dad-11d1-80b4-00c04fd430c8 - URLs
  • 6ba7b812-9dad-11d1-80b4-00c04fd430c8 - OIDs
  • 6ba7b814-9dad-11d1-80b4-00c04fd430c8 - X.500 DNs

Key property: The same namespace + name always produces the same UUID. This is deterministic, not random.

Problem: MD5 is cryptographically broken (collision attacks demonstrated in 2004, CVE-2004-2761). For new systems, use v5 (SHA-1) or a custom scheme with SHA-256. v3 exists for backward compatibility.


v4: Random

UUID v4 uses 122 bits of cryptographically random data. The remaining 6 bits are the version (4 bits set to 0100) and variant (2 bits set to 10).

/generators/uuid/4

Collision probability: With 122 random bits, the search space is 2^122 ≈ 5.3 × 10^36. To reach a 50% probability of collision, you would need to generate approximately 2.71 × 10^18 UUIDs (about 2.7 quintillion). At a rate of one billion UUIDs per second, this would take roughly 86 years. For practical purposes, v4 collision is not a concern.

Structure:

xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx
         ↑                 ↑
    version=4          variant bits

Use case: The default choice for most applications. IDs for database records, API resources, session tokens, distributed system identifiers. The only meaningful drawback is database index fragmentation when used as primary keys in clustered indexes.

Code Examples

<?php
// PHP 9.0+ native UUIDv4
// Or use ramsey/uuid (the standard PHP library):
use Ramsey\Uuid\Uuid;

$uuid = Uuid::uuid4()->toString();
// b1b2b3b4-b5b6-4b7b-b8b9-b0b1b2b3b4b5
import uuid

uid = uuid.uuid4()
print(str(uid))
# 9f5a4c1e-3b2d-4a7f-8c6e-1d2e3f4a5b6c
// All modern browsers and Node.js 14.17+
const id = crypto.randomUUID();
// 'f47ac10b-58cc-4372-a567-0e02b2c3d479'

v5: SHA-1 Name-Based

UUID v5 is identical to v3 in concept but uses SHA-1 instead of MD5. The algorithm:

  1. Concatenate namespace UUID bytes with name UTF-8 bytes
  2. Compute SHA-1 (160 bits)
  3. Truncate to 128 bits, set version to 5, set variant bits

SHA-1 is cryptographically broken for collision resistance (SHAttered attack, 2017), but for UUID generation purposes - where you are simply mapping names to unique identifiers deterministically - it is still acceptable. There is no known practical attack against v5 UUID generation specifically.

Use case: Deterministic IDs where the same input must always produce the same UUID. Content-addressable systems, idempotent API endpoints, deduplication keys. Prefer v5 over v3 for new systems.

import uuid

# Generate v5 UUID for a DNS name
ns = uuid.NAMESPACE_DNS
uid = uuid.uuid5(ns, 'example.com')
print(str(uid))
# cfbff0d1-9375-5685-968c-48ce8b15ae17
# This value is deterministic - always the same for "example.com" in DNS namespace

v6: Reordered Time

UUID v6 reorders the timestamp bits from v1 to make the UUID lexicographically sortable. The timestamp is the same 60-bit Gregorian time value, but the fields are arranged so that the most significant bits come first.

v1 timestamp order: time_low | time_mid | time_hi (not sortable) v6 timestamp order: time_hi | time_mid | time_low (sortable)

This makes v6 UUIDs sort in creation order when stored as text or bytes, which dramatically improves database B-tree index performance for clustered indexes.

Use case: Drop-in replacement for v1 when timestamp extraction and sortability are both required. Preserves MAC address compatibility with v1 systems while fixing the sort order problem.


v7: Unix Timestamp

UUID v7 uses a Unix timestamp (millisecond precision) instead of the Gregorian timestamp used by v1 and v6. This makes the timestamp directly readable without conversion.

/generators/uuid/7

Structure:

unix_ts_ms (48 bits) | ver (4 bits) | rand_a (12 bits) | var (2 bits) | rand_b (62 bits)
  • Bits 0-47: Unix timestamp in milliseconds
  • Bits 48-51: Version 0111 (7)
  • Bits 52-63: 12 random bits (or monotonic counter for same-millisecond ordering)
  • Bits 64-65: Variant 10
  • Bits 66-127: 62 random bits

K-sortability: v7 UUIDs sort correctly by creation time both lexicographically and numerically (within the same millisecond, ordering is random unless the implementation uses a monotonic counter). This makes v7 ideal for database primary keys.

Timestamp extraction:

import uuid
from datetime import datetime, timezone

uid = uuid.uuid7()  # Python 3.12+ or with the uuid7 library
# Extract timestamp from first 48 bits
ts_ms = int(str(uid).replace('-', '')[:12], 16)
dt = datetime.fromtimestamp(ts_ms / 1000, tz=timezone.utc)
// Extract timestamp from a v7 UUID string
function extractTimestampFromUUIDv7(uuid) {
    const hex = uuid.replace(/-/g, '').substring(0, 12);
    return parseInt(hex, 16); // Unix milliseconds
}

Quick Comparison Table

Version Source Sortable Has Timestamp Deterministic Best Use Case
v1 Time + MAC No Yes (Gregorian) No Legacy, timestamp extraction
v2 Time + UID No Yes No DCE only (avoid)
v3 MD5(namespace+name) No No Yes Legacy name-based
v4 Random No No No General purpose IDs
v5 SHA1(namespace+name) No No Yes Deterministic IDs
v6 Reordered time + MAC Yes Yes (Gregorian) No v1 replacement
v7 Unix ms + random Yes Yes (Unix) No Database PKs

Which Version to Choose

Default choice: v4. If you do not have a specific reason to use another version, use v4. It is supported everywhere, generates no information about the issuing system, and has sufficient uniqueness for virtually any application.

Database primary keys: v7. Sequential UUIDs eliminate index fragmentation in clustered indexes (SQL Server, MySQL InnoDB). On PostgreSQL with BRIN indexes, v7 is also significantly more efficient. The improvement over v4 is measurable: insert throughput increases by 2-10x on high-write workloads due to reduced B-tree splits.

Deterministic IDs: v5. When you need the same input to always produce the same UUID - idempotent webhooks, content deduplication, stable references to named resources - use v5. Never use v3.

Timestamp extraction: v7 or v6. v7 is the modern choice. v6 exists for systems that already use v1 and need drop-in sortability.

Never use v2 outside DCE compatibility scenarios.


Conclusion

UUID versions are not interchangeable. v4 is the right default, v7 solves database indexing problems, and v5 handles deterministic ID generation. The choice between them has measurable performance and correctness implications.

Generate a v4 UUID: /generators/uuid/4

Generate a v7 UUID (K-sortable, database-friendly): /generators/uuid/7

More Articles

Password Security and Entropy: Why Length Beats Complexity

A technical guide to password entropy for developers. Covers entropy calculation, character sets, passphrases vs random strings, brute force and rainbow table attacks, and secure password generation.

26 February, 2026

RAG Document Assistant: Answer Questions from Your Own Docs with Ollama, ChromaDB and Docker

Build a local RAG document assistant that reads .txt files, indexes them with vector embeddings, and answers questions using a local LLM — all without a cloud API. Includes a FastAPI backend, a minimal browser UI, and a full Docker Compose setup.

26 February, 2026

Free Local LLM in Docker: Build a Customer Feedback Analyser with Ollama and Pydantic

How to run Ollama in Docker Compose, pull a model on first start, and build a Python CLI that reads customer reviews from CSV, clusters them by theme, and generates a structured report — using Pydantic schemas and system/user message separation. No API keys, no monthly bills.

25 February, 2026