Generate RFC 4122 UUID v4s on Your Own Device
Generate single or bulk RFC 4122 UUID v4 identifiers using hardware entropy from crypto.randomUUID() in browser memory.
UUID v4 Generator
100% In-browser execution. Zero server uploads, instant results, free forever.
- 1. What a UUID v4 Actually Contains
- 2. Why Math.random() Is the Wrong Source
- 3. crypto.randomUUID and the 65,536-Byte Limit
- 4. Collision Probability, With the Actual Numbers
- 5. When v4 Is the Wrong Version: UUID v7 and Database Indexes
- 6. Why Generating Identifiers Locally Is the Sane Default
- 7. Frequently Asked Questions (FAQ)
What a UUID v4 Actually Contains
A UUID version 4 is 128 bits, of which 122 are random and 6 are fixed by the specification. RFC 9562 (which superseded RFC 4122 in 2024) reserves four bits for the version and two for the variant, leaving the rest to chance.
The canonical text form is 36 characters: 32 hexadecimal digits in five hyphen-separated groups of 8-4-4-4-12.
f47ac10b-58cc-4372-a567-0e02b2c3d479
^ ^
| variant bits (first hex digit is 8, 9, a or b)
version (always 4)
Two positions are not random and are worth recognising by eye:
- Character 15 β the first digit of the third group β is always
4. That is the version nibble. - Character 20 β the first digit of the fourth group β is always
8,9,aorb. Those are the two variant bits (binary10) followed by two random bits.
If you are validating UUIDs and your regex accepts any hex in those positions, it will accept strings that are not valid version 4 UUIDs. A correct pattern is specific about both:
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
Why Math.random() Is the Wrong Source
A large number of UUID snippets online generate their randomness with Math.random(). Every one of them is producing identifiers that are predictable in principle.
Math.random() is specified only as an "implementation-dependent algorithm or strategy" returning values with approximately uniform distribution. It is a pseudo-random number generator: a deterministic function of hidden internal state. V8 uses xorshift128+, which is fast and statistically well-distributed and explicitly not cryptographically secure. Given enough consecutive outputs, the internal state can be recovered and all future outputs predicted. This has been demonstrated publicly against V8's implementation.
It matters whenever the identifier is doing security work. If a UUID is a password-reset token, a session identifier, an unguessable document URL, or an invitation code, predictability turns it into an enumeration vulnerability. An attacker who can sample identifiers can generate the next ones.
There is a second, subtler problem. Math.random() returns a double in [0, 1), which carries 52 bits of mantissa but typically only ~32 bits of underlying entropy per call in practice. Composing 122 bits of a UUID from such values does not reliably deliver 122 bits of entropy, which quietly weakens the collision arithmetic in the next section.
The correct source is the Web Crypto API, which is backed by the platform's cryptographically secure generator:
crypto.getRandomValues(new Uint8Array(16));
crypto.randomUUID and the 65,536-Byte Limit
Modern browsers and Node expose a single-call generator that does all of this correctly:
const id = crypto.randomUUID();
// 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
crypto.randomUUID() is available in all current browsers and requires a secure context β HTTPS or localhost. On an insecure origin it is simply absent, which is a common cause of "randomUUID is not a function" on a staging server served over plain HTTP.
When generating in bulk, or when you need the raw bytes for another format, crypto.getRandomValues() is the primitive underneath. It has one limit that catches people out: a single call may request at most 65,536 bytes. Ask for more and it throws a QuotaExceededError. Generating a large batch therefore requires chunking:
function randomBytes(length) {
const output = new Uint8Array(length);
for (let offset = 0; offset < length; offset += 65_536) {
crypto.getRandomValues(
output.subarray(offset, Math.min(offset + 65_536, length)),
);
}
return output;
}
To assemble a v4 UUID from raw bytes by hand, set the two reserved fields explicitly β this is exactly what randomUUID does internally:
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx
Collision Probability, With the Actual Numbers
"Collision-free" is a claim people repeat without arithmetic. The honest statement is that collisions are possible and overwhelmingly improbable, and the birthday bound tells you exactly how improbable.
With 122 random bits, the number of UUIDs you must generate before the probability of any collision reaches 50% is approximately 2^61, which is about 2.3 Γ 10^18 β 2.3 quintillion.
More useful in practice is the probability at realistic volumes. For n identifiers drawn from 2^122 possibilities, the chance of at least one collision is approximately nΒ² / 2^123:
| UUIDs generated | Approximate collision probability |
|---|---|
| 1 billion (10^9) | 1 in 10^19 |
| 1 trillion (10^12) | 1 in 10^13 |
| 100 trillion (10^14) | 1 in 10^9 |
For comparison, the probability of an undetected error in a TCP segment protected by its 16-bit checksum is vastly higher than any figure in that table. In any realistic application, UUID collision is not the risk you should be budgeting attention for.
The caveat that matters: this arithmetic assumes 122 bits of genuine entropy. It is invalid if the generator is Math.random(), and it is invalid on a device whose entropy pool was not properly seeded at boot β a documented historical issue on some embedded platforms. The arithmetic is only as good as the randomness beneath it.
When v4 Is the Wrong Version: UUID v7 and Database Indexes
Version 4 is the right default for identifiers that must be unguessable. It is frequently the wrong choice for a database primary key, and the reason is index locality.
A v4 UUID is uniformly random, so consecutive inserts land at random positions in a B-tree index. Every insert dirties a different page, the working set grows toward the size of the whole index, and page splits multiply. On a large table with a clustered index on a random key β SQL Server and MySQL's InnoDB both cluster by primary key β this shows up as write amplification and steadily degrading insert throughput.
UUID version 7, standardised in RFC 9562, addresses this directly. Its first 48 bits are a Unix millisecond timestamp, with the remaining bits random. The result sorts chronologically, so inserts append to the end of the index the way an auto-increment integer does, while remaining globally unique and generatable without coordination.
Choose by what the identifier is for:
- v4 β public tokens, share links, anything that must not be guessable or enumerable, anything whose creation time must not leak.
- v7 β database primary keys, event and log identifiers, anything where ordering or index locality matters.
Be aware of the trade: a v7 identifier discloses its creation time to anyone holding it. That is often harmless and occasionally not β it can reveal account-creation dates, order volumes over a period, or the timing of internal events. Do not use v7 where the timestamp is sensitive.
Why Generating Identifiers Locally Is the Sane Default
Many online UUID generators post a request to a server and render what comes back. For a value whose only property is that nobody can predict it, that arrangement is difficult to justify.
An identifier generated on a remote server has been known to a system you do not control, has traversed a network, and may exist in that server's request logs. If the UUID is destined to become a session token, an API key or a reset link, its secrecy has already been compromised before you have pasted it anywhere.
Generating in the browser tab removes that exposure. crypto.getRandomValues() draws from the operating system's own entropy source β /dev/urandom on Unix-like systems, BCryptGenRandom on Windows β through the browser's implementation. It is the same source the browser uses for TLS key material.
The OpenTools UUID generator runs entirely in the page for this reason. There is no request carrying the generated value, and batch generation is chunked to respect the 65,536-byte per-call limit rather than silently truncating.
Two habits worth keeping regardless of which tool you use: never reuse an identifier across a security boundary β a UUID that was ever a public document id should not later become an authentication token β and do not treat a UUID as a secret unless it was generated from a cryptographic source, because a v4 UUID from a weak generator provides confidence rather than security.
Frequently Asked Questions (FAQ)
What is a UUID v4?
A UUID version 4 is a 128-bit identifier of which 122 bits are random and 6 are fixed by RFC 9562. It is written as 36 characters in five hyphen-separated hexadecimal groups of 8-4-4-4-12. The first digit of the third group is always 4, marking the version, and the first digit of the fourth group is always 8, 9, a or b, encoding the variant.
Is Math.random() safe for generating UUIDs?
No. Math.random() is a pseudo-random number generator whose internal state can be recovered from enough consecutive outputs, and V8 implements it with xorshift128+, which is explicitly not cryptographically secure. Any UUID used as a session token, reset link or unguessable URL must come from crypto.randomUUID() or crypto.getRandomValues(), which draw from the operating system entropy source.
Why is crypto.randomUUID undefined in my browser?
crypto.randomUUID requires a secure context, meaning HTTPS or localhost. On a page served over plain HTTP the method is simply absent, which is the usual cause of the error on staging servers. Serving the page over HTTPS or testing on localhost restores it.
How likely is a UUID v4 collision?
With 122 random bits you would need to generate roughly 2.3 quintillion UUIDs before the chance of any collision reaches 50%. At one billion identifiers the probability is about 1 in 10 to the power 19. This arithmetic holds only if the randomness is genuine; it is invalid for UUIDs produced with Math.random() or on a device whose entropy pool was poorly seeded.
Should I use UUID v4 or v7 for a database primary key?
Use v7. A v4 UUID is uniformly random, so consecutive inserts land at random positions in a B-tree index, causing page splits and write amplification on large tables. UUID v7 begins with a 48-bit Unix millisecond timestamp, so identifiers sort chronologically and inserts append to the end of the index. The trade-off is that a v7 identifier discloses its creation time to anyone holding it.
Why generate UUIDs in the browser instead of on a server?
A UUID generated remotely has been known to a system you do not control and may persist in that server request logs, which undermines the one property the identifier needs if it will become a token or key. Generating in the page with crypto.getRandomValues draws from the same operating system entropy source the browser uses for TLS key material, and the value never leaves the tab.
Related Guides & Solutions
Base64 Encode and Decode Without Leaking Tokens
Convert Unix Epoch Timestamps to UTC and Local
JSON to Zod Schema, Generated in Your Browser
Ready to use UUID v4 Generator?
Execute this workflow privately on your device right now without creating an account or paying for cloud API credits.