UUID Generator

Version 4 UUIDs straight from the browser's crypto API, up to 100 at a time, ready to paste into seeds, fixtures and config files.

What the 128 bits actually contain

A UUID is 128 bits. In a version 4 UUID only 122 of them are random: four bits are fixed to mark the version and two more mark the variant, which is why the thirteenth hex digit of every v4 identifier is always 4 and the seventeenth is always 8, 9, a or b. You can identify a v4 UUID by eye from those two positions alone.

It is written as 32 hexadecimal digits in five hyphenated groups, 8-4-4-4-12. The hyphens carry no information; they are convention. The canonical form is lowercase, and while most parsers accept uppercase, systems that compare UUIDs as strings rather than as bytes will treat the two casings as different values. A genuine source of duplicate rows.

The governing standard changed recently and a lot of writing has not caught up. RFC 9562, published in May 2024, obsoletes the long-cited RFC 4122. Version 4 is unchanged by it, but the new document adds versions 6, 7 and 8, and version 7 in particular changes the advice below.

The collision argument, done properly

The reason a client can mint identifiers without asking a server is that collisions are not merely unlikely, they are unreachable. With 122 random bits the space holds about 5.3 x 10^36 values.

The number that matters is not the size of the space but the birthday bound, because you are asking whether any two of your identifiers collide, not whether a specific one repeats. Fifty per cent probability arrives at roughly the square root of the space: about 2.3 x 10^18 UUIDs. Generating a billion per second, you would pass that point after seventy years.

So the practical risk is not mathematical, it is implementational. A v4 UUID is only as good as the randomness behind it, and the classic failure is a generator seeded from the clock, or one seeded identically across forked processes or freshly imaged virtual machines. Those systems produce colliding UUIDs within hours. This page uses crypto.randomUUID where available, falling back to crypto.getRandomValues, both of which draw from the operating system's entropy pool rather than a seeded pseudorandom generator.

The cost nobody mentions until the table is large

UUIDs are not free, and the bill arrives at scale. Sixteen bytes against four or eight for an integer is the obvious part, and it is the smaller part. Stored as text rather than binary (a `char(36)` column, which is depressingly common) it is 36 bytes, and every secondary index carries a copy of the primary key, so the cost multiplies across the schema.

The real problem is index fragmentation. A clustered index stores rows in key order. Sequential integers append to the end of the last page. Random UUIDs land uniformly across the whole key space, so nearly every insert targets a different page, forcing that page to be read, split when full, and written back. On a large table this shows up as collapsing insert throughput, a bloated buffer pool full of pages you touched once, and an index that needs rebuilding far too often. MySQL's InnoDB and SQL Server's clustered indexes are both affected; PostgreSQL's heap storage is less exposed, though its indexes still fragment.

This is exactly what UUID version 7 fixes. It puts a 48-bit Unix millisecond timestamp in the high bits and fills the rest with randomness, so identifiers sort roughly by creation time and inserts return to appending near the end of the index. You keep uncoordinated generation and lose the fragmentation. If you are choosing an identifier scheme for a new table today, v7 is usually the better default, and v4 is the right pick when you specifically do not want creation time leaking into the identifier.

Where v4 is exactly right

Test fixtures and seed data, which is what this page is mostly used for. Idempotency keys on API requests, where the client mints a key, retries with the same one, and the server deduplicates. Correlation and trace identifiers threaded through logs across services. Object keys in blob storage, where scattering across the key space is a feature rather than a cost because it spreads load across partitions.

One caution worth stating plainly: a v4 UUID is unguessable but it is not a secret. It is fine as an identifier in a URL and it is not an authorisation mechanism. If knowing the UUID is sufficient to read the resource, you have built a capability URL, which leaks through referrer headers, browser history, proxy logs and shared links. Check permissions on the request rather than relying on the identifier being hard to guess.

Reading and storing them well

Store UUIDs in a native type where the database has one (PostgreSQL's `uuid`, SQL Server's `uniqueidentifier`) or as `binary(16)` where it does not. That halves the storage against text and makes comparisons integer-fast rather than string-fast.

Be careful with SQL Server's `uniqueidentifier` ordering: it does not sort in the byte order you would expect from the text form, which surprises people who assume a v7-style time prefix will cluster there. Test the assumption rather than inheriting it.

The braces option on this page produces the Microsoft registry form, `{xxxxxxxx-..}`, which several Windows APIs and COM interfaces expect. The uppercase option exists for the same reason. Neither changes the value; both change what a naive string comparison will conclude.

ULID, NanoID, and when a UUID is the wrong shape

UUID is not the only uncoordinated identifier, and the alternatives trade different things.

ULID is 128 bits like a UUID but encodes as 26 characters of Crockford base32 rather than 36 of hex, sorts lexicographically by time, and avoids the ambiguous characters I, L, O and U. It predates UUID v7 and solves the same ordering problem; if you are starting today, v7 has the advantage of being in the standard.

NanoID takes the opposite view: a 21-character URL-safe string, 126 bits, no version or variant overhead, and no hyphens to strip. It is the pragmatic choice for identifiers that appear in URLs, where a 36-character UUID is simply ugly and long.

Snowflake identifiers, as used by Twitter and Discord, go further still and drop uncoordinated generation: they pack a timestamp, a machine identifier and a per-machine sequence into 64 bits. Half the storage of a UUID and naturally ordered, at the cost of needing every generator to have a distinct, correctly configured machine identifier, which is a real operational burden and a real source of duplicate-id incidents.

The honest summary is that v4's only genuine advantage is that it carries no information at all. Every alternative leaks creation time, and sometimes machine identity, into the identifier. When that matters (an identifier a customer can see, where the creation order is commercially sensitive) v4 is the correct pick and its index cost is the price.

Frequently asked questions

Could two of these UUIDs ever collide?

Not in practice. With 122 random bits the birthday bound sits near 2.3 x 10^18 identifiers, so generating a billion a second you would need about seventy years to reach a coin-flip chance. Real collisions come from broken randomness, not from the maths.

Are they generated on my device?

Yes, using the browser's own crypto.randomUUID where available and a crypto.getRandomValues fallback otherwise. Nothing is requested from a server and nothing is stored.

Should I use a UUID as a database primary key?

It depends on the version. Random v4 keys fragment a clustered index because every insert lands on a different page. UUID version 7 keeps uncoordinated generation but sorts by time, which restores append-like inserts, usually the better default for new tables.

Why is there always a 4 in the same position?

That digit is the version field, fixed by the standard. The digit after the next hyphen is the variant and is always 8, 9, a or b. Those six bits are why a v4 UUID carries 122 random bits rather than 128.

Is a UUID secret enough to protect a resource?

No. It is unguessable but it is an identifier, not an authorisation. UUIDs leak through referrer headers, browser history and shared links, so check permissions on the request rather than relying on the value being hard to guess.

Uppercase or lowercase?

The canonical form is lowercase. Uppercase parses fine almost everywhere, but any system comparing UUIDs as strings rather than bytes will treat the two casings as different values, which is a real way to end up with duplicates.

What is the difference between UUID v4 and v7?

Version 4 is 122 random bits and carries no information. UUID version 7 replaces the top 48 bits with a Unix millisecond timestamp, so identifiers sort by creation time and stop fragmenting a clustered index, at the cost of leaking when the record was made.