UUID Generator
A UUID is a 128-bit identifier that can be generated independently on any machine without coordination, and still be unique. That property — no central authority, no round trip — is why they underpin distributed systems.
The Versions Are Not Interchangeable
| Version | Built from | Sortable | Use it for |
|---|---|---|---|
| v1 | Timestamp + MAC address | Roughly | Legacy; leaks the host |
| v3 | MD5 of a namespace + name | No | Deterministic IDs from a known name |
| v4 | 122 random bits | No | The general default |
| v5 | SHA-1 of a namespace + name | No | Deterministic, preferred over v3 |
| v7 | Unix timestamp + randomness | Yes | New database keys |
Deterministic UUIDs Are Underused
v5 hashes a namespace and a name, so the same inputs always produce the same UUID on any machine, forever. That makes it ideal for deriving a stable ID from something you already have — a URL, a file path, an external system's key — without storing a mapping table.
``
uuid5(NAMESPACE_URL, "https://example.com/a") -> always the same UUID
`
Generating in Bulk
Seeding a test database, building a fixture set or pre-allocating keys all want a column of
identifiers rather than one. Generating them client-side costs nothing and needs no
round trip — unlike database-generated keys, which force an insert before you know the ID.
Storage: Do Not Use a 36-Character String
A UUID is 16 bytes. Stored as text it is 36 characters, and indexed as text it compares
character by character.
| Storage | Size | Notes |
|---|---|---|
uuid (PostgreSQL) | 16 bytes | Native, correct |
BINARY(16) (MySQL) | 16 bytes | With UUID_TO_BIN() |
CHAR(36) | 36 bytes | 2.25× larger, slower to compare |
On a large table with several UUID foreign keys, that difference is measured in gigabytes.Randomness Source
These are generated with crypto.getRandomValues(), the browser's CSPRNG. A generator
built on Math.random() produces values that look identical and are predictable — which
matters the moment anyone treats an ID as unguessable.
The Avalanche Effect
A one-character change produces a completely different digest — not a similar one. That
property is what makes a hash useful as a fingerprint:
| Input | MD5 | CRC32 |
|---|---|---|
hello | 5d41402abc4b2a76b9719d911017c592 | 3610a686 |
hello. | d94c10e437d18531e122ed0b45badd2a | 0a39d4f1 |
Hello | 8b1a9953c4611296a827abf8c47804d7 | f7d18982 |
hello and Hello differ by one bit of one byte, and share no part of their output.
RIPEMD-160 of hello is 108f07b8382412612c048d07d13f814118445acd, and of Hello is
d44426aca8ae0a69cdbc4021c64fa5ad68ca32fe` — same story.Digest Length and Collision Resistance
| Algorithm | Output | Birthday bound | Status |
|---|---|---|---|
| CRC32 | 32 bits | ~77,000 values | Checksum only |
| MD5 | 128 bits | 2⁶⁴ in theory | Broken — collisions in seconds |
| SHA-1 | 160 bits | 2⁸⁰ in theory | Broken — SHAttered, 2017 |
| RIPEMD-160 | 160 bits | 2⁸⁰ | No practical attack |
| SHA-256 | 256 bits | 2¹²⁸ | Current standard |
| SHA-512 | 512 bits | 2²⁵⁶ | Standard, faster on 64-bit |
Never Hash a Password With These
A general-purpose hash is designed to be fast, which is exactly wrong for passwords: speed helps the attacker. Use a deliberately slow KDF — bcrypt, scrypt or Argon2id — with a per-password salt. A GPU tries billions of SHA-256 guesses a second and a few thousand bcrypt guesses a second, and that gap is the entire defence.