f47ac10b-58cc-4372-a567-0e02b2c3d479 and 1ee89e58-1dd2-11b2-8080-808080808080 are both valid UUIDs, both 128 bits, both formatted the same way — and both were built by completely different methods that leak different information and behave differently once they hit a database. Most people generate a UUID, pick whatever version their library defaults to, and move on. That's fine most of the time, but the two most common versions — v1 and v4 — trade off privacy, sortability, and database performance in ways that matter once you're past a prototype.
What's Actually Inside a UUID
A UUID is a 128-bit number, almost always written as 32 hex digits split into five groups: 8-4-4-4-12. Two of those bits positions are reserved to encode metadata: a version nibble (which algorithm built it) and a variant (which spec it follows — nearly always the RFC one). Everything else is payload, and what goes into that payload is exactly what separates the versions.
- v1 — payload is a 60-bit timestamp (100-nanosecond intervals since Oct 15, 1582, for historical reasons) plus a 48-bit "node ID," which is supposed to be the generating machine's MAC address.
- v4 — payload is 122 random bits, full stop. No timestamp, no machine identity, nothing derived from anything.
Same shape, same length, opposite philosophy: v1 is deterministic and traceable, v4 is pure noise.
UUID v1: Time-Based, and It Knows Where It Came From
A v1 UUID is built from the current timestamp and the machine's network hardware address. That has one genuine upside — the timestamp component means v1 UUIDs generated in sequence are roughly time-ordered, which you don't get for free with v4.
The downside is the reason v1 fell out of favor for anything public-facing: it embeds the generating machine's MAC address in the ID itself. Anyone who has a v1 UUID can extract the exact millisecond it was created and the network hardware identifier of the machine that made it. That's a real privacy leak if the UUID ever ends up in a URL, an API response, or a support ticket a customer can see — it's effectively broadcasting internal infrastructure details. Most v1 implementations today substitute a random 48 bits for the node ID instead of the real MAC specifically to avoid this, but that's a workaround bolted onto the spec, not what v1 was designed to do.
UUID v4: Private, But Not Free Either
v4 has no timestamp and no machine identity — it's 122 bits of randomness, which is why it's the default in almost every UUID library today. The tradeoff shows up somewhere else: database performance.
Primary keys and indexes are typically stored as B-trees, which perform best when new values are inserted in roughly ascending order — new rows land at the end of the tree, and recently-inserted rows (the ones you're most likely to query next) stay clustered together in cache. A random v4 UUID inserted as a primary key lands at a random point in the index every single time, which fragments the B-tree, forces more disk pages into memory, and measurably slows down inserts and range queries as a table grows. This is a well-documented issue in Postgres and MySQL alike — it's not theoretical, it's why time-ordered ID schemes (and eventually UUID v7, standardized in 2024 specifically to fix this) exist at all.
So Which One Should You Use?
| UUID v1 | UUID v4 | |
|---|---|---|
| Payload | Timestamp + machine ID | Pure random |
| Roughly sortable by creation time | Yes | No |
| Leaks machine/network info | Yes (unless node ID is randomized) | No |
| Database index-friendly | Yes | No — fragments B-tree indexes |
| Good for public-facing IDs (URLs, API responses) | No | Yes |
| Good default when you're not sure | No | Yes |
Default to v4 for anything a user or client can see — order IDs, API resource IDs, file IDs, session-adjacent identifiers. It leaks nothing about your infrastructure and the collision odds are close enough to zero to ignore (you'd need to generate roughly 2.7 quintillion v4 UUIDs before hitting a 50% chance of a single collision).
Only reach for v1 in closed, internal systems where you specifically want the built-in time ordering and you've confirmed the node ID isn't a real MAC address (check your library's docs — many randomize it by default now, some don't). If you need time-ordering and public-safety, that's specifically the gap UUID v7 was created to fill — worth knowing about even if it's not yet as universally supported as v1/v4.
Two Pitfalls Worth Knowing
"UUIDs are guaranteed unique." Not literally — the collision probability is astronomically low, not zero. For any system where an actual collision would be catastrophic (rare), that distinction matters; for everything else, it's a non-issue.
"I can sort UUIDs as strings to get chronological order." Only true for v1, and only if you reorder the bytes correctly — the timestamp isn't stored in the same order it's displayed in, so naively sorting v1 UUIDs as text does not give you chronological order. v4 gives you no ordering information at all, by design.
Generating Either One
UUID Generator generates both v1 and v4 UUIDs on demand, in bulk, free — pick the version, pick a count, and copy the results. Useful for seeding test data, generating IDs during development, or just checking what a given version actually looks like before wiring it into your schema.
Quick FAQ
Can two UUIDs actually collide? Mathematically yes, practically no — for v4 specifically, you'd need quintillions of UUIDs generated before collision odds become non-negligible. It's not worth designing around for typical applications.
Is UUID v4 safe to use as a security token (session ID, password reset link)? Only if your language's UUID library uses a cryptographically secure random number generator (CSPRNG) — most modern ones do, but it's implementation-dependent, not guaranteed by the v4 spec itself. If you're generating a security-sensitive token, use your language's dedicated secure-token function rather than assuming any UUID v4 generator is safe for that purpose.
Should I use a UUID or an auto-incrementing integer for a database primary key? Auto-increment integers are faster to index and smaller to store, but don't work well across distributed systems where multiple services generate IDs independently — UUIDs solve that at the cost of index performance (see above). Many teams split the difference: an internal auto-increment integer plus a public-facing UUID.
Why does my UUID library default to v4 instead of v1? Because v4 is safe to expose publicly with no privacy leak and no configuration to get wrong, which makes it the safer default even though it costs you sortability.
Related Free Tools
- UUID Generator — generate v1 or v4 UUIDs in bulk, instantly
- Hash Generator — MD5, SHA-1, SHA-256 and more for when you need a deterministic identifier instead
- Password Generator — for actual security tokens, not repurposed UUIDs
- JSON Formatter — inspect API payloads that carry UUID fields