UUIDs Explained: Versions, Collisions and When to Use Them
What a version 4 UUID actually contains, how small the collision risk really is, and when a UUID is the wrong choice for a key or a secret.
What a UUID is, and what is inside a version 4 one
A UUID (universally unique identifier) is a 128-bit number, normally written as 32 hexadecimal digits split into five groups: 8-4-4-4-12. Here is a typical one: 3f2b8c1e-9a4d-4b6e-8c21-5d7e0a9f1b34. The point of the format is that any computer can create an identifier on its own, with no central counter, and expect it to differ from every other identifier ever created.
Not all 128 bits are free. In a version 4 UUID, 4 bits record the version and 2 bits record the variant, which leaves 122 bits chosen at random. You can see the markers in the text. The first digit of the third group is always 4, and the first digit of the fourth group is always 8, 9, a or b. In the example above that is 4b6e and 8c21.
The UUID v4 generator follows this layout. It fills 16 bytes from the browser's secure random source, forces the version and variant bits, and prints the result in the 8-4-4-4-12 form, in lowercase. It produces one UUID per click and nothing is sent to a server.
How likely is a collision?
With 122 random bits there are 2122 possible values, about 5.3 × 1036. The chance of any two UUIDs matching grows with the number you create, following the birthday problem. Using the standard approximation, the probability of at least one duplicate among n random UUIDs is about n² divided by 2 × 2122. That gives these figures:
| UUIDs generated | Chance of at least one duplicate |
|---|---|
| 1 million | about 1 in 1025 |
| 1 billion | about 1 in 1019 |
| 1 trillion | about 1 in 1013 |
| 1 quadrillion (1015) | about 1 in 10 million |
| about 2.7 quintillion (2.7 × 1018) | about 50% |
To put the last row in context: producing a billion UUIDs every second for about 85 years gives you roughly that many, and only then does a duplicate become a coin flip. No normal application comes close. A table with a billion rows has a duplicate risk far smaller than the risk of a disk fault or a bug in your own code.
These numbers assume the random source is good. The real danger is not mathematics but a weak generator, for example one seeded identically on cloned virtual machines, or code that uses a predictable pseudo-random function. That is why the tool relies on the browser's cryptographic generator rather than Math.random(). If you write your own UUID code, use your platform's UUID library or its secure random API.
The versions in plain terms
- Version 1 combines a timestamp with a node identifier, historically derived from a network address. It sorts roughly by time but can leak when and where it was created.
- Version 4 is random. It reveals nothing about when or where it was made, and it is the default in most libraries.
- Version 7 starts with a Unix timestamp in milliseconds and fills the rest with random bits, so values created later sort after earlier ones. It was standardised in RFC 9562, published in May 2024, which replaced the older RFC 4122.
Our generator only produces version 4. If you need version 7, use your database's or language's library.
When a UUID is a good choice
- Keys created in several places. Mobile apps working offline, multiple services, or data merged from different databases can all assign IDs without asking a central server.
- Public identifiers. A URL such as
/orders/8d3e...does not tell a visitor how many orders you have, unlike/orders/1042. - Idempotency keys and request IDs. A client generates one per action and the server can safely ignore a repeat.
- Test fixtures and temporary file names. Quick, unique and collision-free.
When a UUID is the wrong choice
- As a password or access token. Unique is not the same as secret. A properly random v4 UUID has 122 bits of unpredictability, which is plenty in theory, but UUIDs get logged, copied into URLs, shown in support tickets and generated by libraries whose randomness you did not check. For a credential, use a dedicated generator such as the password generator, or your framework's token API, and treat the value as a secret.
- As an authorisation check. Hiding a record behind a hard-to-guess ID does not replace checking that the logged-in user may see it.
- When humans must read or type the ID. Thirty-six characters are painful over the phone. Use a short human-friendly reference alongside the UUID.
- When insert speed on a huge table matters. Random keys land all over a database index, which causes more page splits and cache misses than steadily increasing keys. For very large tables, a time-ordered UUID or a sequential key can perform noticeably better. Measure with your own data before deciding.
A decision framework
- Will records be created in more than one place without coordination? If no, a plain auto-increment integer is simpler, smaller and faster.
- If yes, will the table grow to many millions of rows with heavy writes? If yes, consider version 7 or another time-ordered scheme. If no, version 4 is fine.
- Do you need the ID to be unguessable? Then add a real authorisation check anyway, and use a separate secret token if the ID itself must act as a key.
- Does the ID appear in a URL or log? Remember it is not secret once it does.
Storing UUIDs without wasting space
As text, a UUID takes 36 characters with hyphens, or 32 without. As raw binary it takes 16 bytes. Across a billion rows that is about 36 GB versus 16 GB for the key column alone, before indexes, which repeat the key. Where your database has a native UUID type, use it. Choose one letter case and stick to it, since the value is the same either way but string comparisons are not. The generator outputs lowercase.
Checking a value with a regex
If you need to confirm that a string looks like a version 4 UUID, you can try a pattern in the regex tester. In that tool, type the pattern, then a line containing only ---, then the text to test. For example:
[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}
The tester runs with the global flag only and is case-sensitive, so this pattern will not match an uppercase UUID; change each a-f to a-fA-F for that. The pattern checks shape, not whether the ID exists, and it does not anchor to the start and end of the text, so in production code add anchors.
Limits of this post and the tool
- The generator creates one version 4 UUID at a time. It does not make version 1 or version 7 values, and it has no bulk mode, so large batches need a library or a database function.
- It does not check whether a UUID already exists in your system. At the odds above that is rarely a concern, but a unique constraint on the column is cheap insurance.
- The collision figures here are approximations that assume a sound random source. They say nothing about a faulty generator.
- Performance advice depends on your database and workload. Treat the index discussion as a prompt to test, not a rule.
This article is general technical information and not a substitute for reviewing your own system's design and security requirements.