Skip to content
ansezz.

▸ Free tool

UUID Generator.

Crypto-random v4 UUIDs, generated by your browser's own CSPRNG. Set how many, generate, copy — nothing is sent anywhere.

▸ This makes v4, not v7

Great for test data, seeds, correlation IDs and idempotency keys. If these are going to be primary keys in a large table, generate v7 or ULID at the source instead — the reason is below.

What a v4 UUID actually is

A UUID is 128 bits, printed as 32 hex digits in an 8-4-4-4-12 grouping. In version 4, six of those bits are not yours: four say "this is version 4" and two mark the variant. The other 122 come straight out of a random source. You can read both fields off the string without a parser: the third group starts with the version digit, always 4 here, and the fourth group starts with the variant digit, always 8, 9, a, or b.

This page calls crypto.randomUUID() and falls back to crypto.getRandomValues() with those six bits set by hand. No Math.random() anywhere, which turns out to be the whole ballgame.

The collision maths, in plain terms

122 random bits is about 5.3 × 10³⁶ values, but the number that matters is the birthday bound, which sits near the square root of that. Concretely: generate 103 trillion v4 UUIDs and your odds of a single duplicate anywhere in the pile are roughly one in a billion. You are not going to collide.

What you can break is the assumption underneath the maths: a real CSPRNG. Duplicates in the wild come from Math.random(), from a PRNG in a process that forked after seeding, from images that boot with an identical entropy pool, and from code that truncates a UUID to fit a legacy column. Keep the unique constraint anyway — not as a retry mechanism, but as documentation of intent.

The version decides your write pattern

The versions are not quality tiers, they are different trade-offs. v1 embeds a timestamp and a node ID that is traditionally the machine's MAC address — it leaks both, and because the timestamp fields sit out of order, it does not even sort by time. v4 is pure randomness. v7, standardised in RFC 9562 in 2024, leads with 48 bits of Unix milliseconds, big-endian, then randomness.

  v1 v4 v7
What the 128 bits hold 60-bit timestamp + 14-bit clock sequence + 48-bit node ID 122 random bits 48-bit millisecond timestamp + up to 74 random bits
Sorts by creation time
Leaks something MAC address and creation time Creation time, by design
Good primary key at scale
Where it is specified RFC 4122, kept in RFC 9562 RFC 4122, kept in RFC 9562 RFC 9562 (2024)

That ordering is not cosmetic — it is an index problem, and it is the production failure people actually hit. A primary key is a B-tree key. Time-ordered values append to the rightmost leaf page, which stays hot in the buffer pool. Random v4 values target a random page every time: the working set becomes the entire index, pages split down the middle instead of filling up, and the moment the index outgrows RAM your insert throughput falls off a cliff. MySQL with InnoDB suffers worst, because the table is the primary-key B-tree and every secondary index carries a full copy of that key.

This tool generates v4. For keys in a table that will get large, reach for v7 or ULID where the row is created — uuidv7() is built into PostgreSQL 18 and there is a library for every language. ULID is the same idea (48 bits of milliseconds plus 80 random) in a 26-character base32 encoding that sorts as text; v7 wins on being a real UUID that drops into a uuid column.

Store 16 bytes, not 36 characters

A UUID is 128 bits. The canonical text form is 36 characters, so char(36) spends 36 bytes to hold 16 bytes of information. PostgreSQL has a native uuid type; MySQL does not, so use binary(16) with UUID_TO_BIN() and BIN_TO_UUID() at the edges; SQL Server has uniqueidentifier.

The 20 wasted bytes sound trivial until you remember that InnoDB copies the full primary key into every secondary index — five indexes, five more copies of the waste, in RAM as well as on disk.

When an auto-increment integer still wins

A bigint is 8 bytes, strictly monotonic, readable in a log, and half the width of a UUID in every index and foreign key. One database, one writer, IDs that never appear in a URL — that is the correct choice, and a UUID there is pure tax.

UUIDs earn their place when you need the ID before the insert (client-side creation, offline-first sync, idempotency keys), when you merge rows from several sources, when you shard, or when the ID is public — /orders/1024 tells a competitor your order count and invites a stranger to try 1025. Most teams land on both: a bigint primary key for joins, plus an indexed UUID column for the outside world.

Questions, answered.

Are UUIDs guaranteed to be unique?

Not guaranteed — just overwhelmingly unlikely to collide. A v4 UUID carries 122 random bits, and the birthday bound puts you at roughly a one-in-a-billion chance of a single duplicate after generating 103 trillion of them. The real risk is never the maths, it is the random source: values drawn from Math.random(), from a PRNG seeded identically across forked workers, or from a library that truncates the UUID to fit a column will collide long before probability says they should.

Are the UUIDs from this generator cryptographically random?

Yes. The page calls crypto.randomUUID(), which the Web Crypto spec requires to be backed by a cryptographically secure random source, and falls back to crypto.getRandomValues() with the version and variant bits set by hand where randomUUID() is not available. Both need a secure context, so HTTPS or localhost. Nothing is generated on a server and nothing is sent anywhere — load the page once and it keeps working offline.

Does this tool generate UUID v7?

No. This generates v4 only, and I would rather say that than pretend otherwise. v7 puts a 48-bit millisecond timestamp at the front so the values sort by creation time, which is exactly what you want for database primary keys — so generate it where the row is created, not by pasting from a web page. PostgreSQL 18 ships a uuidv7() function, and every major language has a library.

Should I use a UUID as a database primary key?

It depends on the version and the write volume. Random v4 keys scatter inserts across the whole B-tree instead of appending to the rightmost page, which causes page splits, index fragmentation, and a sharp drop in insert throughput once the index stops fitting in memory. It hurts most on MySQL with InnoDB, where the table itself is the primary-key B-tree and every secondary index stores a full copy of that key. If you want a UUID primary key, use a time-ordered one: v7 or ULID.

How should I store a UUID in MySQL or PostgreSQL?

As 16 bytes, never as a 36-character string. PostgreSQL has a native uuid type that stores the raw 128 bits; MySQL has no UUID type, so use binary(16) with UUID_TO_BIN() and BIN_TO_UUID() to convert at the edges. A char(36) column wastes 20 bytes on every row, and in InnoDB that waste is repeated inside every secondary index, on top of slower string comparison under a collation.

Can I use a UUID as an API key or a password reset token?

A v4 UUID from a proper CSPRNG has 122 bits of entropy, so guessing one is not the problem. The format is. UUIDs get logged, pasted into URLs, and leaked through Referer headers, and nothing about the shape of a UUID tells the next developer that this particular one is a secret. Generate a purpose-made random token instead, keep it out of URLs, and store only its hash.

Keep going

Keep reading