UUID generator

Generate UUIDs one at a time or fifty at once — random v4, or time-ordered v7, whose timestamp keeps a batch sortable and makes it a better database key.

Version

122 random bits. Reveals nothing about when it was created.

How many

Generating…

What a UUID is and why it exists

A UUID — Universally Unique Identifier, also called a GUID in Microsoft documentation — is a 128-bit value written as 32 hexadecimal digits in the familiar 8-4-4-4-12 grouping. Its whole purpose is to let separate systems mint identifiers independently, with no coordination between them, and still be confident the results will not collide. That is what makes it different from a database auto-increment column: two servers, two mobile clients offline on a plane, and a background job can all create records at the same moment without asking anyone for permission.

Uniqueness here is probabilistic rather than guaranteed. Version 4 leaves 122 bits to randomness, which is a large enough space that generating billions of values still leaves the chance of a repeat far below the chance of a disk silently corrupting the data instead. In practice you can treat it as unique; the maths is not the weak link.

Version 4 vs. version 7 — the choice that matters

Version 4 is random from end to end. It carries no information whatsoever: not when it was made, not by whom, not in what order. That is either its greatest strength or its central flaw, depending on where you put it.

Version 7, standardised in RFC 9562 in 2024, replaces the first 48 bits with a millisecond Unix timestamp and fills the rest with randomness. Because the time comes first and the value is read left to right, sorting v7 identifiers as plain text also sorts them chronologically.

This is not a cosmetic difference. Databases keep primary keys in a sorted B-tree index. Insert v7 keys and each new row lands at the right-hand edge of the tree, next to the previous one — the pages you are writing to stay in memory and the index grows tidily. Insert v4 keys and every write lands at a random position, so the database touches a different page each time, cache hit rates fall and the index fragments. On a large, busy table the difference in insert throughput is substantial, which is why v7 has been adopted so quickly for primary keys.

  • Choose v7 for database primary keys, event and log identifiers, and anything you will want to sort or range-query by creation time.
  • Choose v4 when the identifier appears somewhere untrusted and must leak nothing — including the fact that one record was created just before another.
  • Either is fine for a correlation ID, an idempotency key or a file name, where ordering does not matter.

The trade-off is exactly that leak: a v7 identifier tells anyone who sees it, to the millisecond, when it was created. For a row ID that is usually harmless and often useful. For a password-reset token or a public share link it is information you did not mean to publish — and those should not be UUIDs at all, but random tokens from a dedicated secret generator.

Ordering within the same millisecond

A subtlety that catches many v7 implementations: modern hardware generates far more than one identifier per millisecond. If two values share a timestamp, their order is decided by the random bits that follow — which is to say, at random. Generate fifty in a tight loop and you get fifty values that are only roughly ordered, losing precisely the property you chose v7 for.

RFC 9562 addresses this with a monotonic counter, and this tool implements it: the 12 bits immediately after the timestamp count upward within a millisecond, so a batch is strictly increasing rather than approximately so. If the counter fills — more than 4096 values in the same millisecond — the generator borrows the next millisecond instead of wrapping around and emitting a value that sorts before its predecessor. It handles the clock moving backwards, which happens with NTP corrections, the same way.

Reading a v7 value left to right, taking 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b as the example:

  • 0190a1b2-c3d4 — the 48-bit Unix timestamp in milliseconds. Because it comes first, text order is time order.
  • 7 — the version nibble, which is what makes this a v7 rather than a v4.
  • e5f — the 12-bit monotonic counter, incrementing within a single millisecond.
  • 8 — the variant bits, fixed by RFC 9562 for every modern UUID (always 8, 9, a or b).
  • a9b-0c1d2e3f4a5b — the remaining 62 bits, purely random.

Storing and using UUIDs

The canonical form is lowercase with hyphens, and RFC 9562 says generators should emit exactly that — which is why this tool offers no formatting options. You will still meet other spellings in the wild: uppercase and wrapped in braces in Microsoft tooling, and stripped of hyphens where someone wanted a shorter column. They are all the same 128 bits, and comparisons should be case-insensitive.

Where it counts is storage. A UUID is 16 bytes, but the text form is 36 characters — so storing it as a string more than doubles the space in the row and, more importantly, in every index that includes it. Use a native type where one exists:

uuid                      -- PostgreSQL: native 16-byte type
BINARY(16)                -- MySQL: compact; CHAR(36) wastes 20 bytes/row
uniqueidentifier          -- SQL Server
crypto.randomUUID()       // JavaScript: v4 only, needs a secure context
uuid.uuid4() / uuid7()    # Python: stdlib v4; v7 via a library

One last caution: a UUID identifies, it does not authorise. Because they are unguessable, it is tempting to treat an unlisted URL containing one as private. But identifiers leak — through logs, browser history, referrer headers and screenshots — so anything that actually needs protecting still needs a real permission check behind it.

How to tell whether a UUID is v4 or v7

You are usually handed an identifier with no note saying where it came from, and you do not need one: the version is written into the value. Both are 32 hexadecimal digits in the same 8-4-4-4-12 grouping, so the difference is not in the shape — it is two single digits at fixed positions, and everything else follows from them.

  • The first digit of the third group is the version nibble. A 4 there means version 4, a 7 means version 7, and no other part of the value has a say in it.
  • The first digit of the fourth group carries the variant bits, and it is 8, 9, a or b in both versions — so it never tells you which one you are holding. What it does tell you is that the value follows RFC 9562 at all; anything else in that position is an older layout or not a UUID.
  • If that nibble is a 7, the first twelve digits are the creation time: a 48-bit count of milliseconds since the start of 1970, written in hexadecimal.
  • If it is a 4, there is nothing further to read. A v4 encodes no time, no machine and no order, which is the property you chose it for.

Take the value the section above decodes, 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b. Its third group opens on a 7, so it is a version 7, and its first twelve digits are 0190a1b2c3d4 — convert that hexadecimal number to decimal, hand it to the Unix timestamp converter, and you have an afternoon in July 2024. Beside it, 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d opens its third group on a 4, and its own first twelve digits are random data that decodes to nothing. The two agree on exactly one thing: both open their fourth group on one of the four digits the variant bits allow.

Is v7 as collision-proof as v4?

It is a fair thing to ask, because v7 genuinely holds less randomness. A v4 spends 122 of its 128 bits on random data. A v7 spends 48 on the timestamp and, here, 12 more on the monotonic counter, which leaves 62 random bits — a little over half as many. Read as a bare number that looks like a serious downgrade.

  • Within one batch from this tool a repeat is impossible rather than unlikely. Values that share a millisecond get different counter values, and values that do not share a millisecond have different timestamps, so no two can be equal — that is arithmetic, not probability.
  • Between two machines generating at the same instant, the odds are at worst the birthday problem over 62 bits, which turns even at roughly the square root of the space: in the region of two billion values, all created in the same single millisecond.
  • Across different milliseconds a v7 collision is not merely unlikely but impossible, because the leading digits themselves differ.

So the honest comparison is not 122 bits against 62. It is one lottery drawn over and over for the whole life of the system against a separate, much smaller lottery held inside each millisecond and thrown away when the millisecond ends. The second arrangement is the stronger one, and the reason to prefer v4 remains the one the comparison section gives — not collisions, but that a v7 says out loud when it was made.

Switching an existing table from v4 to v7

The question that follows choosing v7 is what to do about the rows already in the table, and the reassuring part is that the column does not have to change at all. Both versions are the same 128 bits in the same 36-character text form, so a PostgreSQL uuid column, a BINARY(16) or a CHAR(36) holds a mixture without noticing. You start generating v7 for new rows and stop there; there is no migration step and no backfill.

  • What arrives immediately: every row inserted from now on carries a leading timestamp, so new keys land beside each other at one end of the index instead of scattering across it. That benefit is about where new writes go, and it is there on the first insert.
  • What never arrives: the rows already there stay unordered for good. Nothing can put a creation time into a value that was never given one, and re-issuing every identifier means rewriting every foreign key that points at one — a far larger job than changing a generator, and rarely worth it for an index alone.
  • What to watch: sorting the column stops meaning one thing. A count of milliseconds is a small number for a 48-bit field, so the v7 keys gather in a narrow band low in the range while the v4 keys are spread over the whole of it, with the occasional one falling in among them.

That last point is the one that bites, because a query that sorts by the key looks right on new data and quietly misreports the old. If you need one order over the whole table, add a created-at column and sort on that, and let the identifier go back to being an identifier. The mixture is at least readable while you do it: the version nibble sits in every value, so a query can tell the two eras apart without a second column when it has to.

Frequently asked questions

Should I use v4 or v7?
Use v7 for database primary keys and anything you will sort by creation time: the leading timestamp keeps inserts clustered at the end of the index instead of scattered across it. Use v4 when the identifier must reveal nothing at all, including when it was created.
Are two UUIDs ever the same?
It is possible but vanishingly unlikely. Version 4 has 122 random bits, so even after generating billions of values the probability of a collision stays far below the probability of the storage silently corrupting them instead.
What happened to versions 1, 3 and 5?
Version 1 encodes a timestamp and the machine's MAC address, which leaks hardware identity and cannot be produced in a browser at all. Versions 3 and 5 derive a UUID deterministically from a namespace and a name using MD5 or SHA-1 — useful when the same input must always produce the same identifier, but a different job from generating a fresh one.
Is a UUID secure enough to use as a secret token?
A v4 UUID is unguessable, but a v7 one openly encodes its creation time, and neither is meant as a credential. For password resets, session tokens or share links, generate a dedicated random secret and check permissions on the server rather than relying on the identifier being hard to guess.
Why is my v7 batch not perfectly sorted in other tools?
Because many implementations skip the monotonic counter. When several values share a millisecond, their order falls to the random bits that follow. This tool implements RFC 9562's counter, so a batch generated here is strictly increasing.
How should I store a UUID in a database?
In a native 16-byte type where one exists — uuid in PostgreSQL, uniqueidentifier in SQL Server, BINARY(16) in MySQL. Storing the 36-character text form instead more than doubles the space used in the row and in every index that includes the column.
Are these generated on your servers?
No. They are generated in your browser using crypto.getRandomValues, the platform's cryptographic random source — the same one crypto.randomUUID uses. No value is ever sent anywhere, and reloading the page produces an entirely fresh set.
Can I tell when a v7 UUID was created?
Yes, and the value is all you need. Its first twelve hexadecimal digits are a count of milliseconds since the start of 1970: convert them to decimal and hand the result to the Unix timestamp converter. A v4 has no such field, so the same twelve digits there are random and decode to nothing.
Does v7 have fewer random bits than v4?
Yes — 62 here against v4's 122, because the timestamp and the counter take the space. It does not make a collision likelier in practice: two v7 values can only ever collide if they were created in the same millisecond, so those 62 bits are spent inside one millisecond rather than across the whole life of the system, and within a single batch from this tool the counter makes a repeat impossible rather than merely unlikely.
Can v4 and v7 UUIDs live in the same column?
Yes. They are the same 128 bits in the same text form, so nothing about the schema changes and there is no migration — you generate v7 from now on and leave the old rows alone. The one thing to watch is sorting: the new rows are in creation order among themselves, but the older random ones are scattered around them, so ordering by the key is not a time order for the table as a whole.
What are UUID versions 6 and 8?
RFC 9562 defines both alongside v7. Version 6 is version 1 with its timestamp fields reordered so that the value sorts chronologically, meant for systems already committed to v1 — the RFC says anything else should use v7 instead. Version 8 is a deliberately open slot for custom layouts, in which only the version and variant bits are fixed and the remaining 122 are yours to define. This tool generates neither.

Related tools