tooldura

Developer Tools

Which UUID Version to Use, and Why v7 Changed the Answer

T
tooldura editorial
9 min readUpdated August 22, 2026Open tool →

For twenty years the answer was v4, because the alternative broadcast your network card's address to anyone holding the id. RFC 9562 landed in May 2024 with versions 6, 7 and 8, and it changed the recommendation for one specific and very common case: when the UUID is going to be a primary key.

What Is Actually in a UUID

128 bits, written as 32 hex digits in a 8-4-4-4-12 pattern. Six of those bits are not yours: four say which version this is, two say which variant of the specification it follows.

You can read both by eye. The first digit of the third group is the version, so in f81d4fae-7dec-11d0-a765-00a0c91e6bf6 the 1 after the dash makes it a v1. The first digit of the fourth group encodes the variant, and for every UUID you will meet in practice it is 8, 9, a or b, meaning the RFC's own variant. Anything else is a Microsoft GUID from before the standard settled, or not a UUID at all.

That leaves 122 bits to carry meaning, and what a version does with them is the entire difference between the versions.

Every Version, and What It Puts in Those Bits

Versions 2 and 8 are omitted: v2 is a DCE Security variant almost nothing implements, and v8 is a blank slate for vendor-defined layouts.

VersionContentsReach for it when
v160-bit timestamp, clock sequence, node idYou are joining a system that already uses it. Otherwise no
v3MD5 of a namespace plus a nameSomething already depends on MD5. Use v5 instead
v4122 random bitsThe default. Anything that does not need ordering
v5SHA-1 of a namespace plus a nameThe id must be reproducible from a name, on any machine
v6v1's fields, timestamp reordered to sortYou are migrating an existing v1 system and want ordering
v748-bit Unix ms timestamp, then 74 random bitsThe id is a database key, or anything indexed or sorted

Why a Random Key Costs You

A database index is a B-tree, and a B-tree is kept in sorted order on disk. Insert a row with a key that sorts after everything already there and the write lands in the rightmost page, which is already in memory because the last insert touched it too. That is the sequential case, and it is close to free.

Insert a random key and it lands wherever it happens to sort, which is a page the database probably has to read from disk first. Once that page fills, it splits in two, leaving both halves half-empty. Do this a few million times and the index has grown well past the size the same data would occupy in insertion order, while the pages your working set needs no longer fit in the buffer pool.

MySQL's InnoDB makes this worse than most, because its tables are physically clustered on the primary key: the whole row lives in the leaf page, so a random primary key scatters the table itself, not just an index. PostgreSQL stores rows in a heap and keeps the index separate, so it suffers the index fragmentation without the table fragmentation.

None of this is an argument against UUIDs. It is an argument against *random* UUIDs in the one position where sort order maps to physical layout.

🕵️

The UUID that identified a virus author

Version 1 put the machine's MAC address in the last 48 bits, on the reasoning that a globally unique network card guarantees a globally unique id. It also guarantees a globally unique fingerprint. When the Melissa macro virus spread in March 1999, investigators pulled the GUIDs Microsoft Word had embedded in the document and used them, along with other evidence, to tie the file to David L. Smith, who was arrested within a week. RFC 9562 section 6.10 now says to use random bits with the multicast bit set instead, which is what any browser-based generator has to do anyway, since no web page can read a MAC address.

What v7 Actually Does

The layout is deliberately boring: 48 bits of Unix timestamp in milliseconds, then the four version bits, then 12 bits, then the two variant bits, then 62 more bits of randomness. The timestamp is first, so comparing two v7 UUIDs as bytes, or as text, compares them by creation time.

48 bits of milliseconds runs out around the year 10889, which is not a problem anyone reading this will have.

The subtlety is what happens inside a single millisecond. A thousand ids created in the same millisecond share the same leading 48 bits and are then ordered by pure randomness, which is exactly the fragmentation you switched away from v4 to avoid, just at a smaller scale. RFC 9562 section 6.2 answers this by allowing those 12 bits after the version to be a counter that increments within the millisecond, and a generator that skips it is only approximately sortable. This site's generator uses the counter, so a batch of a hundred comes out strictly increasing rather than merely grouped.

One thing v7 gives up: the creation time of every record is now readable by anyone holding the id. Usually that is fine or even useful. If your ids are exposed to users and the timing is sensitive, that is the trade you are making.

Generate a UUID in any version

v4, v7, v1, v6, name-based v3 and v5, plus the nil and max constants.

Open UUID Generator →

Decisions That Follow

Once the version is settled, the rest of the choices tend to get made badly by default.

1

Store 16 bytes, not 36 characters

PostgreSQL has a native `uuid` type that occupies 16 bytes. MySQL does not, so use `BINARY(16)` rather than `CHAR(36)`, which more than doubles the storage and makes every index comparison longer.

2

Do not reorder v4 bytes to fake sorting

The old MySQL trick of shuffling a v1's timestamp fields into sortable order is exactly what v6 standardised. Use v6 or v7 rather than a custom byte swap nobody else can read.

3

Only v4 is unguessable

A v7 is mostly timestamp, and a v1 is timestamp plus a node id that never changes. Neither is a secret. If the id doubles as an access token, it has to be v4, and even then a purpose-built token is the better answer.

4

Pick v5 over v3

They differ only in the hash, and MD5 has no reason to be in new code. Neither is a security decision, since both truncate to 122 bits, but v5 is the one the RFC points at.

5

Keep the hyphens unless something forbids them

The canonical form has them, every parser expects them, and stripping them saves four bytes in a text column you should not be using anyway.

A UUID Is Not a Password

RFC 9562 is direct about this in its security considerations: do not assume a UUID is hard to guess. The specification covers versions whose contents are almost entirely predictable, and a system that treats "has the UUID" as "is authorised" is relying on a property only one version happens to have.

The exception, stated precisely, is a v4 from a cryptographically secure source. 122 random bits is more entropy than a 20-character password, and the browser's crypto.randomUUID() is backed by the same generator as the rest of Web Crypto. That is genuinely unguessable.

What it is not is revocable, rotatable or scoped. An unguessable URL is still a URL, and it will end up in a referrer header, a proxy log and someone's chat history. Use it for an unlisted share link where the consequence of a leak is proportionate. Do not use it where you would otherwise have asked for a login.

Frequently Asked Questions

Related Tools

Keep Reading