What Is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit value used to identify objects without central coordination. Every UUID is represented as 32 hex digits split into 5 groups by hyphens:
550e8400-e29b-41d4-a716-446655440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
↑
version nibble (M)
The RFC standard (originally RFC 4122, superseded by RFC 9562 in 2024) defines eight UUID versions. Developers use three of them: v1, v4, and the newer v7. The rest are niche. Understanding what's in those 128 bits determines which one to pick.
UUID v4 — The Safe Default
UUID v4 fills 122 of its 128 bits with cryptographically random data. The remaining 6 bits encode the version and variant. That's it. No timestamp, no node info, no hidden structure.
How it's generated: The OS's CSPRNG (the same entropy source used by TLS) fills
a 128-bit buffer. Two fields are then fixed: bits 12–15 are set to 0100 (version 4),
and bits 62–63 are set to 10 (RFC 4122 variant). Everything else is random.
UUID v4 is the right choice for:
- Session identifiers and authentication tokens
- API keys (for low-security scenarios; use a token library for production auth)
- File names when you need global uniqueness
- Any identifier that doesn't need to sort chronologically
- Identifiers generated client-side before a database insert
The only real downside of UUID v4 is database performance when used as a primary key — which UUID v7 was designed to fix.
UUID v1 — Timestamp + MAC Address
UUID v1 was the original standard. It encodes a 60-bit timestamp (100-nanosecond intervals since October 15, 1582 — yes, the Gregorian calendar reform date) plus a 48-bit node identifier, which is typically the generating machine's MAC address.
// v1 structure
xxxxxxxx-xxxx-1xxx-yxxx-xxxxxxxxxxxx
↑
48-bit node (MAC address or random)
The privacy problem: The node field leaks the MAC address of the machine that generated the UUID. Anyone who receives a UUID v1 can identify the exact network card — and by extension, trace multiple UUIDs back to the same machine. This was one of the discoveries in the Melissa worm investigation in 1999, and it's been a known issue ever since.
When v1 is still used: Internal distributed systems where traceability to a
node is desirable rather than a liability. Some legacy databases store v1 UUIDs. Apache Cassandra
natively generates v1 UUIDs via now(). If you're working with Cassandra or an
existing v1 schema, you keep using v1 — just don't expose them publicly.
The clock sequence problem: If two UUIDs are generated within the same 100-nanosecond window on the same node, v1 increments a clock sequence counter to avoid collision. If the system clock moves backward, the clock sequence is randomized. This means v1 is monotonic within a node under normal conditions but not guaranteed globally.
UUID v7 — The Modern Choice
UUID v7 was finalized in RFC 9562 (May 2024). It takes the best parts of v1 (time-orderability) and v4 (privacy, random suffix) and combines them:
// v7 structure
xxxxxxxx-xxxx-7xxx-yxxx-xxxxxxxxxxxx
↑ ↑
48-bit Unix ms 74 random bits
The 48 most-significant bits encode the current Unix timestamp in milliseconds (not the 1582 epoch used by v1). The remaining 74 bits (minus version/variant) are cryptographically random.
This gives you:
- Chronological sort order — UUIDs generated later sort after earlier ones
- Privacy — no MAC address, no node info, just a timestamp and randomness
- Millisecond precision — multiple v7 UUIDs generated in the same millisecond are ordered by their random suffix (not guaranteed to sort within-ms, but random isn't a problem)
- Drop-in compatibility — same 36-character string format, same UUID column type
Side-by-Side Comparison
| Property | v1 | v4 | v7 |
|---|---|---|---|
| RFC | RFC 4122 (2005) | RFC 4122 (2005) | RFC 9562 (2024) |
| Generation method | Timestamp + MAC address | 122-bit CSPRNG random | 48-bit Unix ms + 74-bit random |
| Chronologically sortable | Yes (per node) | No | Yes (globally) |
| Privacy-safe | No — leaks MAC address | Yes | Yes |
| DB index performance | Good (per node) | Poor (random) | Excellent |
| Random bits | ~14 bits (clock seq) | 122 bits | 74 bits |
| Browser native support | No | Yes (crypto.randomUUID) | No (library required) |
| Recommended for new projects | No | Yes (non-DB use) | Yes (DB primary keys) |
The Database Performance Story
This is where UUID version choice actually matters at scale. Every database engine that uses a B-tree index (PostgreSQL, MySQL, SQLite, SQL Server) organizes index entries in sorted order on disk. When you insert a new row, the database places its key in the correct position in the B-tree.
With UUID v4 as a primary key: Each new UUID inserts into a random position in the B-tree. The database must find a page in the middle of the index, read it, insert the new key, and potentially split the page if it's full. Page splits are expensive: they require writing two new pages to disk and updating pointers. At high insert volumes, this becomes a bottleneck — index pages fragment, cache hit rates drop, and VACUUM has more work to do.
With UUID v7: New UUIDs always sort after previous ones (within millisecond precision). The database appends to the rightmost B-tree leaf page. No page splits, no fragmentation, predictable sequential I/O. The performance profile matches an integer autoincrement.
Code Examples
JavaScript / Node.js
// UUID v4 — built into browsers and Node.js 19+
const id = crypto.randomUUID();
// "a5f3c8b2-4e1d-4a07-8c9b-2d1e3f4a5b6c"
// UUID v7 — requires the uuid npm package (v10+)
import { v7 as uuidv7 } from 'uuid';
const id = uuidv7();
// "018f1234-abcd-7ef0-8a12-3456789abcde"
// ↑ first 12 chars encode current Unix ms
// UUID v1 — also in the uuid package
import { v1 as uuidv1 } from 'uuid';
const id = uuidv1();
Python
import uuid
# UUID v4 — built-in
id = str(uuid.uuid4())
# "a5f3c8b2-4e1d-4a07-8c9b-2d1e3f4a5b6c"
# UUID v1 — built-in
id = str(uuid.uuid1())
# UUID v7 — requires python-uuid7 package
# pip install uuid7
from uuid_extensions import uuid7
id = str(uuid7())
# PostgreSQL v7 via psycopg (server-side)
# SELECT gen_uuid_v7() -- requires pg_uuidv7 extension
Go
// UUID v4 — google/uuid
import "github.com/google/uuid"
id := uuid.New() // v4
// "a5f3c8b2-4e1d-4a07-8c9b-2d1e3f4a5b6c"
// UUID v7 — google/uuid v1.6+
id, err := uuid.NewV7()
if err != nil {
log.Fatal(err)
}
// UUID v1
id, err := uuid.NewUUID() // v1
PostgreSQL
-- UUID v4 — built into Postgres 13+
SELECT gen_random_uuid();
-- UUID v7 — requires the pg_uuidv7 extension
-- https://github.com/fboulnois/pg-uuidv7
CREATE EXTENSION IF NOT EXISTS pg_uuidv7;
SELECT uuid_generate_v7();
-- Use as a default column value
CREATE TABLE users (
id UUID DEFAULT uuid_generate_v7() PRIMARY KEY,
email TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
Which Version Should You Use?
The answer depends on what you're doing with the UUID:
- Database primary keys on a new table: Use UUID v7. The index performance improvement is free. There's no downside.
- Session IDs, API tokens, file names: Use UUID v4. You don't need sort order, you need randomness and unpredictability.
- Existing Cassandra or v1 schema: Stay on UUID v1. Migration isn't worth it unless you have a specific reason.
-
Client-side ID generation (React, Vue, browser apps): Use UUID v4
via
crypto.randomUUID()— it's available natively. v7 requires a library. - Distributed ID generation across multiple services: Use UUID v7. The millisecond prefix gives you global sort order without coordination. v1's per-node sort is not globally meaningful when nodes have different MAC addresses.
What About UUID v3 and v5?
Both use a namespace + name to produce a deterministic UUID. You give them the same input, you get the same UUID back. v3 uses MD5 as the hash; v5 uses SHA-1. Always prefer v5 over v3 since MD5 is considered broken for cryptographic purposes (though the collision risk is low in practice for this use case).
These are useful when you need idempotent ID generation — for example, converting a URL into a stable UUID that's the same every time you process that URL. They're not for general-purpose unique ID generation.
UUID Anatomy Quick Reference
| Version | Bit layout (128 bits) | Sortable | Typical use |
|---|---|---|---|
| v1 | 60b timestamp + 14b clock seq + 6b var + 48b MAC | Per-node | Legacy, Cassandra |
| v3 | MD5(namespace + name) truncated to 128b | No | Deterministic IDs (deprecated) |
| v4 | 122b random + 6b version/variant | No | General purpose, tokens, sessions |
| v5 | SHA-1(namespace + name) truncated to 128b | No | Deterministic IDs, URL hashing |
| v6 | v1 bits reordered for sortability | Yes (but still has MAC) | v1 migration path — rarely used |
| v7 | 48b Unix ms + 74b random + 6b version/variant | Yes (globally) | DB primary keys, distributed IDs |
Ready to generate UUIDs? Our free UUID Generator supports v4 and v1 — completely in-browser, no signup, no limits.
Open UUID GeneratorFrequently Asked Questions
import { v7 as uuidv7 } from 'uuid'; const id = uuidv7(); — Node.js's built-in crypto.randomUUID() only generates v4.00000000-0000-0000-0000-000000000000 — all 128 bits set to zero. It is used as a sentinel "null UUID" in some protocols and databases. It is not generated by any UUID version algorithm; it is a special constant defined in RFC 4122.