BV
All tools
utility

UUID Generator

Generate UUID v1, v4, v5 and ULID identifiers, singly or in bulk, entirely in the browser. Includes measured sortability and index-locality tests, the RFC 9562 bit layouts, and the collision arithmetic for v4.

Muhammad Bilal
Muhammad Bilal Virk
15 min read
Live tool
Identifiers
Output will appear here

Generate single or bulk identifiers in UUID v1, v4 or v5, or ULID for a time-ordered value. Everything happens in the browser, so nothing you generate is sent anywhere. The notes below cover which formats genuinely sort, what each one leaks, and why RFC 9562 now points at v7.

What This Generator Does, And Who Needs It

This tool generates identifiers in several formats: UUID v1, v4, v5 and ULID, singly or in bulk, entirely in the browser. Nothing is sent anywhere, which matters if you are generating keys that will end up in production data.

It is aimed at whoever has to make one decision that is very hard to reverse later: which identifier format goes in the primary key column. Change your mind after a million rows exist and you are writing a migration, rewriting every foreign key and reissuing every URL that contained an ID.

UUID Generator — illustration

This page has been rewritten because the version of it that stood here was out of date. It presented the choice as UUID v4 against ULID and recommended ULID when you wanted time ordering. That was reasonable advice in 2022. It is not now. RFC 9562 was published in May 2024 and obsoletes RFC 4122, and it standardises UUID v7, which is the time-ordered format the whole ULID and Snowflake family was invented to provide. The RFC is explicit: "Implementations SHOULD utilize UUIDv7 instead of UUIDv1 and UUIDv6 if possible."

The RFC also explains itself. In preparing it the authors analysed sixteen separate community implementations solving the same problem in slightly different ways, naming ULID, Snowflake, KSUID, Flake, XID, ObjectID, CUID and nine others. UUID v7 is the standardised outcome of that survey.

Everything below is measured rather than asserted, and where a claim comes from a specification the specification is quoted.

Worked Example One: Measuring Whether Sorting Actually Works

"Time ordered" and "lexicographically sortable" are claims you can test rather than accept. Here is that test, run in Python 3.10, generating 200 identifiers of each type roughly two milliseconds apart, sorting them as plain strings, and counting inversions: pairs where the string order disagrees with the creation order. With 200 items there are 19,900 pairs to check, so a perfectly ordered format scores 0 and a purely random one should score about half.

Format Inversions out of 19,900 Percentage wrong Sample
UUID v4 9,795 49.2% 6552897c-b083-4da3-956d-9d20ba8f06ad
UUID v7 0 0.0% 01a02adb-9bde-7900-a884-c8823aa9d337
ULID 0 0.0% 01M0NDQ6YYZAMN00VKZ6HCHJ2M

Both v7 and ULID sort correctly, and v4 is exactly as unordered as coin flipping. What the old page missed is that v7 gets the same result inside the standard UUID format, so every library, database type and validator that already accepts a UUID accepts it unchanged.

The second half of the measurement is the more useful one, because it is the reason time ordering is worth anything at all. Generate 1,000 identifiers and count how many distinct leading-byte buckets they land in, a rough proxy for how many index pages a run of inserts touches:

Format Distinct leading-byte buckets touched, out of 256
UUID v4 251
UUID v7 1

A thousand v4 inserts scatter across almost the whole keyspace, so the index is dirtied everywhere and the working set is the entire index. A thousand v7 inserts land in one place. RFC 9562 section 6.11 states the consequence plainly: "Time-ordered monotonic UUIDs benefit from greater database-index locality because the new values are near each other in the index," and "the real-world differences in this approach of index locality versus random data inserts can be one order of magnitude or more."

That is the actual argument for v7. It is not aesthetics and it is not that sortable IDs look tidy in a log. It is write amplification on the primary index.

Worked Example Two: The Monotonicity Claim That Both Specs Quietly Withdraw

This is the part almost every UUID page gets wrong, and the sales pitch for time-ordered IDs is noticeably stronger than the guarantee.

Generating 1,000 UUID v7 values in a tight loop, with the 74 non-version bits filled with random data as the base specification allows, produced values spanning only 4 distinct milliseconds, with 373 of them sharing a single millisecond. Inside that one millisecond, there were 36,838 inversions out of 69,378 pairs, or 53%. Within a millisecond, random-filled v7 is no more ordered than v4.

This is not a bug. RFC 9562 section 5.7 says the 74 bits are filled with random data "to provide uniqueness", then offers an alternative: implementations "MAY" instead use a sub-millisecond timestamp fraction of up to 12 bits plus "an OPTIONAL carefully seeded counter", specifically "to guarantee additional monotonicity within a millisecond". Monotonicity inside the tick is opt-in.

ULID has the same gap, and its own specification contradicts itself about it. The feature list at the top claims "Monotonic sort order (correctly detects and handles the same millisecond)". The Sorting section, further down the same document, says "Within the same millisecond, sort order is not guaranteed". Both are true of different code paths: plain ulid() is not monotonic within a tick, and monotonicFactory() is, by incrementing the random component by one bit with carrying. The monotonic version also throws an error rather than wrapping if you exhaust the random space within a single millisecond.

The practical reading: if you insert a few thousand rows per second and only care about ordering to the millisecond, plain v7 is fine. If you need a strict total order you need a counter, and the honest place to get one is the database rather than the application. RFC 9562 section 6.13 agrees: "Applications using a monolithic database may find using database-generated UUIDs (as opposed to client-generated UUIDs) provides the best UUID monotonicity."

How Each Version Is Actually Built

Every version is 128 bits with 4 bits spent on the version field and 2 on the variant field. What fills the rest is the entire difference.

v1 carries a 60-bit timestamp, a clock sequence, and a node field which per RFC 9562 "consists of an IEEE 802 MAC address, usually the host address". That is measurable. A v1 generated on this sandbox gave 2ee8f8b6-9e5c-11f1-a93f-2ba30664db4f, whose node field decodes to the MAC 2b:a3:06:64:db:4f, matching the machine's own uuid.getnode() exactly. The RFC's Security Considerations are blunt about it: "MAC addresses pose inherent security risks around privacy and SHOULD NOT be used within a UUID."

v4 is 122 random bits, and the RFC says so in those words: an implementation "MAY choose to randomly generate the exact required number of bits for random_a, random_b, and random_c (122 bits total)".

v3 and v5 are deterministic hashes of a namespace plus a name. v3 uses MD5. v5 computes SHA-1 over the namespace ID concatenated with the name, uses "the most significant, leftmost 128 bits" and discards "the remaining 32 least significant, rightmost bits". The four registered namespaces are fixed values from the RFC: DNS is 6ba7b810-9dad-11d1-80b4-00c04fd430c8, URL is 6ba7b811-..., OID is 6ba7b812-... and X500 is 6ba7b814-.... Note the gap: the RFC says 6ba7b813-9dad-11d1-80b4-00c04fd430c8 is undefined and "SHOULD NOT be used".

Determinism is exact and unforgiving, which is both the point and the trap. Measured here:

Input v5 in the DNS namespace
bilalvirk.com 0a4d6b0a-8a53-5573-9b9f-38f92bb63291
bilalvirk.com again 0a4d6b0a-8a53-5573-9b9f-38f92bb63291
BILALVIRK.COM 5a6543b1-b41a-5bd4-9348-0068a7455caa
bilalvirk.com. 6d88221b-1523-5227-9e05-c41ef630815f

Same string, same UUID, forever, on any machine, in any language. Change the case or add a trailing dot and you get an unrelated identifier. If you use v5 you must normalise the input first and write down the normalisation rule, because that rule is now part of your data model.

v7 puts a 48-bit big-endian Unix millisecond timestamp in the leading bits, then the version, then 12 bits of rand_a, then the variant, then 62 bits of rand_b. That is why it sorts: the sortable part is at the front, in network byte order. The timestamp is recoverable with no library at all, which is worth seeing as a plain fact rather than a warning: 01a02adb-6d55-7ae3-9b1f-c3a0721bb98e decodes to 1787425418581 milliseconds, which is 2026-08-22 19:03:38 UTC.

v6 is v1 with the timestamp fields reordered so it sorts. It exists for systems already committed to v1. v8 is the deliberately open slot for vendor-specific layouts, and the RFC is careful that "UUIDv8 is not a replacement for UUIDv4".

Choosing A Version

Version Built from Sorts by time What it reveals Standing under RFC 9562
v1 Timestamp, clock sequence, MAC address Not as a string Creation time and the host MAC address Superseded; MAC addresses SHOULD NOT be used
v3 MD5 of namespace and name No The input, to anyone who can guess it Defined, but MD5; prefer v5
v4 122 random bits No Nothing Correct default when ordering does not matter
v5 SHA-1 of namespace and name No The input, to anyone who can guess it The right choice for reproducible IDs
v6 v1 with reordered timestamp Yes Same as v1 For v1 migrations only; v7 preferred
v7 48-bit Unix ms plus 74 bits Yes, to the millisecond Creation time to the millisecond Recommended for new time-ordered IDs
ULID 48-bit Unix ms plus 80 random bits Yes, to the millisecond Creation time to the millisecond Not a UUID version; a separate specification

ULID still has two real advantages and it is worth naming them rather than pretending v7 wins on everything. It is 26 characters rather than 36, and its Crockford base32 alphabet deliberately excludes I, L, O and U, which makes it materially safer to read aloud or transcribe by hand. If your identifiers appear on invoices or get dictated over the phone, that is a genuine reason to keep it. If they only ever live in a database and a URL, v7 wins on compatibility, because it is a UUID and everything already understands UUIDs.

The Collision Question, With Actual Numbers

People worry about v4 collisions far more than the arithmetic justifies. With 122 random bits the space is 2^122, about 5.317 x 10^36. Using the birthday approximation, the probability of at least one collision after generating n values:

Identifiers generated Probability of any collision
1 million 9.4 x 10^-26
1 billion 9.4 x 10^-20
1 trillion 9.4 x 10^-14
1 quadrillion 9.4 x 10^-8

A 50% chance of a single collision needs roughly 2.7 x 10^18 identifiers, more than a system generating a million per second would produce in 86,000 years.

The caveat that matters is the source of randomness, not the width of the space. RFC 9562's Security Considerations note that using an inadequate random source "will result in a vulnerability". A v4 from a poorly seeded generator has nothing like 122 bits of real entropy, and that is the failure mode seen in the wild rather than the birthday bound. Use the platform's cryptographic generator and the arithmetic above applies; build one out of Math.random() and it does not.

Section 6.7 frames the decision usefully too, contrasting a collision that "generated a duplicate log entry, which results in incorrect statistics" with one where "a duplicate key causes an airplane to receive the wrong course". Most systems are firmly in the first category, and should stop optimising for the second.

Common Mistakes

Installing a UUID package to generate v4. crypto.randomUUID() is built into browsers and Node. Per MDN it has been Baseline widely available across browsers since March 2022, returns "a randomly generated, 36 character long v4 UUID", and uses "a cryptographically secure random number generator". The one constraint is secure contexts, so HTTPS and localhost but not plain HTTP. A dependency for one line of built-in functionality is a dependency you will still be patching in three years.

Assuming your language already has v7. Standard library support lags the RFC. Measured on this sandbox, Python 3.10's uuid module has no uuid6, uuid7 or uuid8 function at all, only v1, v3, v4 and v5. Check before you plan around it, and if it is missing, either take a maintained library or implement the layout above, which is 48 bits of timestamp and two bit-masking operations.

Using v1 anywhere the identifier is visible. It publishes the generating machine's MAC address and its creation time to anyone who reads the value. If you have inherited v1 identifiers and need ordering, v6 exists precisely so you can move without abandoning the shape.

Using v5 as a primary key. RFC 9562 section 6.13 warns against this directly, and its reasoning is about schema design rather than cryptography: designers assume "that a particular value will never change, which later turns out to be an incorrect assumption. Postal codes, license or other identification numbers, and numerous other such identifiers seem unique and unchanging at a given point time, only later to have edge cases where they need to change." When the name changes, the key changes, and the key was supposed to be the one stable thing in the row. Use v5 for deduplication keys and cache keys, and a time-based UUID for the primary key.

Storing a random UUID as char(36). This is the worst of both choices. A UUID is 16 bytes of binary and 36 bytes of text. Across 100 million rows that is 1,600 MB against 3,600 MB, before indexes, and you are paying it for a value no human reads. If you are going to store text anyway, at least pick a version whose text form sorts.

Parsing identifiers for meaning in application code. RFC 9562 section 6.12 recommends treating UUIDs "as opaquely as possible". Extracting the v7 timestamp to display a creation date is how you end up unable to change format later, because something now depends on the internals. Store the timestamp in its own column; columns are cheap.

Frequently Asked Questions

Should I use UUID v4 or v7 for a new project?

If the identifier is a database primary key, v7, because of the index locality measured above. If it is a public token, a session identifier, an idempotency key or anything where the creation time should not be inferable, v4, because v7 tells anyone holding the value when it was made to the millisecond. Both are standard, both fit the same column, and it is entirely reasonable to use both in the same application for different purposes.

Is ULID still worth using now that v7 exists?

For a new system where identifiers only travel between machines, no; v7 gives you the same ordering inside a format your database, ORM and validators already support. Keep ULID where its shorter, human-safer text form is doing real work, meaning identifiers that get read aloud, printed or typed in by hand. If you already run ULID at scale, there is no urgency: it is a working 128-bit time-ordered identifier and the migration would cost more than it returns.

Do I need an npm package to generate UUIDs?

Not for v4. crypto.randomUUID() is built into Node and every current browser, in secure contexts. You need a library for v3, v5, v6 and v7, because the platform does not provide them, or you can implement v7 from the bit layout above in about ten lines.

Can two UUID v4 values ever collide?

Mathematically yes, practically no, provided the randomness is real. The table above puts a trillion identifiers at roughly a 9.4 x 10^-14 chance of a single collision. The realistic risk is a weak random source rather than the size of the space, so use the platform's cryptographic generator rather than a general-purpose pseudorandom function.

Does a v7 UUID leak information?

It reveals its own creation time to the millisecond, and that is by design, since the timestamp is the leading 48 bits and needs no key to read. RFC 9562 judges this "a very small attack surface" and notes it "does not define anything about the data itself". The order-of-creation disclosure is the real consideration: sequential identifiers make it possible to reason about volume and timing. For anything enumerable or externally visible, prefer v4.

Should I store UUIDs as text or binary?

Binary if the database has a native type for it, since it is 16 bytes rather than 36 and, per RFC 9562, "may result in faster data access". Text if the value is read straight out and passed on unchanged, which "may make it simpler to implement". The RFC presents this as a genuine trade-off rather than a rule. What is not a trade-off is combining text storage with a randomly ordered version, which costs you space and index locality simultaneously.

Where The Identifier Stops Being The Problem

Most people arriving on a UUID page are not really choosing an identifier. They are dealing with duplicates: the same webhook delivered twice, a retry that created a second record, two systems disagreeing about whether an event was processed.

An identifier alone does not fix that. What fixes it is generating one at ingestion, storing it under a unique constraint, and checking it before acting, and each of those three steps is usually missing rather than wrong. That pattern is worked through end to end in Python and FastAPI Webhook Automation, and the retry side of it, which is where the duplicates come from in the first place, in Retrying A Failed n8n Node. If you are here because v5 looked like a way to derive a stable key from a payload, the hash generator will show you what you are actually hashing, and the password generator covers the other half of this topic, which is when a random value needs to be unguessable rather than merely unique.

Where this stops being a tool problem is when the deduplication layer has to be built rather than described: an ingestion endpoint that assigns and enforces its own idempotency keys, a schema whose primary keys were chosen for how they will be written rather than how they look, and the migration from whatever is in there now. That is the kind of work I take on, and it is usually a matter of days rather than weeks, because the decisions on this page are the hard part and the code around them is small. If you have a pipeline that occasionally processes the same thing twice and you would rather it did not, book a call and bring what your ingestion path looks like now.

Muhammad Bilal
Muhammad Bilal Virk
AI automation engineer — building agents, workflows, and RPA that remove repetitive work.
Share
Newsletter

One email, when I ship something worth reading.

No cadence, no filler. Unsubscribe any time.

Free consultation

Want this built against your real numbers?

A 30-minute call to scope the workflow, agent, or automation you actually need.

Book a free consultation

More utility tools

All tools
Next step

Have a workflow that's burning hours every week?

Bring me one real bottleneck. I'll tell you whether it's worth automating, and what it would take.

Book 30 Minutes Call