What is actually inside a UUID

A UUID is 128 bits, written as 32 hexadecimal digits in five hyphen-separated groups of 8-4-4-4-12. But not all 128 bits are free. Two fields are fixed by the specification:

FieldPositionValue in version 4
VersionFirst digit of the third groupAlways 4
VariantFirst digit of the fourth groupAlways 8, 9, a or b

Four bits of version plus two bits of variant leaves 122 random bits, not 128. That is why every version 4 UUID you generate has a 4 in the same place — a useful sanity check when debugging, and a reminder that a UUID is a structured value rather than a random blob.

The collision numbers, stated honestly

Collision risk follows the birthday problem, approximately n² ÷ (2 × 2^122):

UUIDs generatedProbability of any collision
1 billionabout 1 in 10^19
1 trillionabout 1 in 10^13
1 quadrillionabout 1 in 10 million
2.3 × 10^18about 50%

Put in the usual way: generating a billion UUIDs every second for around 85 years gets you to roughly even odds of a single duplicate. For any real application the random-chance risk is not worth modelling.

But that arithmetic has a precondition, and it is where real collisions come from. It assumes 122 bits of genuinely unpredictable randomness. When duplicate UUIDs do turn up in production — and they do — the cause is almost never the birthday bound. It is entropy:

Browsers now expose a correct implementation directly, and using it removes the question. If you are generating UUIDs anywhere else, the thing worth checking is the random source, not the collision maths.

The versions, and what each is for

VersionBuilt fromProperty
1Timestamp plus network card addressTime-ordered but leaks the machine and creation time
3 and 5Hash of a namespace plus a nameDeterministic — same input always gives the same UUID
4RandomUnguessable, unordered
7Millisecond timestamp plus randomUnguessable in its tail and sortable by creation time

Version 1 carries a genuine privacy cost that is easy to overlook: the identifier embeds the hardware address of the machine that made it, along with the moment it was made. This is not theoretical — it was famously used in 1999 to trace the author of the Melissa virus through identifiers embedded in the documents. Version 1 UUIDs in a public API leak infrastructure details.

Versions 3 and 5 are underused. Because they are deterministic, hashing a namespace and a stable name produces the same UUID every time, which gives you idempotent identifiers without a lookup table — useful for de-duplicating imports where the source has a natural key but no identifier.

Versions 6, 7 and 8 were standardised in RFC 9562, which replaced the older RFC 4122. Version 7 is the one most projects now want, for the reason in the next section.

Why random UUIDs make bad primary keys

The old advice — that UUIDs are unique so you can safely use them as primary keys — is true about uniqueness and misleading about consequences.

A database index is a tree kept in sorted order. Sequential integers append to the end of it, touching one page repeatedly, which stays in memory. Random UUIDs insert at random positions, so every insert touches a different page:

EffectSequential keyRandom UUID key
Pages touched per insertUsually one, hot in cacheA different one each time
Page splitsRareFrequent
Index fragmentationLowHigh
Working set to stay fastSmallApproaches the whole index

On low insert volumes this does not matter. On high-volume tables it produces exactly the kind of slowdown that appears months after launch and is hard to attribute.

Storage compounds it. A UUID is 16 bytes as binary. Stored as a 36-character text column — an extremely common mistake — it becomes 36 or 37 bytes, and that inflation is repeated in every secondary index and every foreign key referencing it:

RepresentationBytes
32-bit integer4
64-bit integer8
UUID as binary16
UUID as fixed-length text36

The fixes, in order of preference: use version 7 so identifiers are time-ordered and insert near the end of the index; store as the native UUID type or as 16 raw bytes rather than text; or keep a monotonic internal key for clustering and expose the UUID as a separate indexed column. Version 7 solves most of this with a one-line change and keeps the useful property that identifiers can be generated by the client without a round trip.

A UUID is not a secret

Version 4 UUIDs are unguessable, which tempts people into using them as bearer tokens — the unlisted link, the password-reset URL, the invitation. That works up to a point, and the point is worth knowing.

An identifier in a URL appears in server logs, in referrer headers sent to third parties, in browser history, in shared screenshots and in link previews generated by chat applications. It does not expire on its own, it cannot be scoped, and revoking it means invalidating the resource. For anything short-lived and low-value that is an acceptable trade; for account recovery it is not, and such tokens should be separately generated, hashed at rest, scoped and given a short expiry.

Version 1 and version 7 UUIDs are additionally partly predictable, since the timestamp portion is known, so neither should ever be used as a capability token.

Honest limits and interoperability notes

UUIDs are canonically lowercase but comparison is case-insensitive, so a system that compares them as raw strings will fail against a source that emits uppercase. Microsoft-style GUIDs are often uppercase and sometimes wrapped in braces, both of which must be normalised away.

There is also a byte-order trap. The Microsoft binary representation stores the first three fields in little-endian order, while the specification defines them big-endian. The same GUID therefore has two different byte layouts, and moving binary values between a .NET system and a standards-compliant one without conversion produces identifiers that look scrambled — a real and frequently painful interop bug.

Finally, they are not human-friendly. Thirty-six characters cannot be read over a phone, quoted in a support ticket reliably, or typed without error. Where a person has to handle the identifier, a short human-readable code alongside the UUID is worth the extra column.

Questions people actually ask

Could I really never see a duplicate?

From chance alone, effectively never. From a broken random source, quite easily — which is the risk actually worth guarding against.

Version 4 or version 7?

Version 7 for database identifiers, because of the index behaviour above. Version 4 where ordering would leak information you would rather not disclose, such as signup sequence or volume.

Is a UUID enough to protect a private link?

Only for low-value, short-lived things. It is unguessable but it is not confidential, because URLs travel further than people expect.

Are the UUIDs generated here recorded?

No. They are produced by your browser cryptographic random source and nothing is transmitted, logged or stored. Copy what you need before closing the page.

Keep exploring Gen Code Tools

Every tool comes with a written guide, and every category is one click away.