How to Generate a UUID v4 (and When NOT to Use It)
Generate a UUID v4 in one line across browser, Node, Python, and CLI — and learn the three cases where UUID v4 is the wrong identifier to reach for.
How to Generate a UUID v4 (and When NOT to Use It)
You need a unique identifier for a row, a file, or a request trace. You search "uuid generator" and land on ten sites that all claim to produce random UUIDs. Some hand you a weak identifier from a broken PRNG; a few log every UUID to an analytics backend. This guide shows how to produce a cryptographically secure UUID v4 in every environment you'll touch — and the three places UUID v4 is the wrong answer.
What a UUID v4 actually is
A UUID (Universally Unique Identifier) is a 128-bit value, written as 32 hexadecimal digits separated into five groups: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. The format comes from RFC 4122, and the updated superset RFC 9562 published in May 2024 adds versions 6, 7, and 8. UUID v4 is the random variant: 122 bits of randomness with 6 fixed bits encoding the version and variant.
The anatomy of a v4 UUID
Here's a typical v4, with the structural bits called out:
f47ac10b-58cc-4372-a567-0e02b2c3d479
^ ^
| └── variant bits: 10xx (RFC 4122)
└──────── version nibble: 4
Every UUID v4 will have a 4 as the first character of the third group, and the first character of the fourth group will always be 8, 9, a, or b. Everything else is random. If a generated string breaks those rules, it isn't a valid v4.
Version bits and variant bits explained
The 128 bits are split by the RFC as follows:
- 48 bits of randomness (before the version nibble)
- 4 bits for the version (always
0100for v4) - 12 more bits of randomness
- 2 bits for the variant (always
10for RFC 4122) - 62 more bits of randomness
That leaves 122 usable random bits — enough that the universe is likely to burn out before you see a collision, provided the randomness is real.
Collision odds you can ignore
The birthday paradox tells us when to start worrying. For 2^122 possible values, the 50% collision threshold is roughly 2^61 — about 2.7 quintillion UUIDs. To put that in perspective, if you generated one billion UUIDs per second on every CPU core in every datacenter on Earth, you'd still need roughly 85 years to reach even a 1% chance of a single collision. This is why engineers treat UUID v4 collisions as mathematically impossible in practice — it's more likely that your hard drive silently flips a bit and corrupts the record first.
How to generate UUID v4 in every environment
You almost never need a library for this. Every modern runtime has a one-liner.
In the browser
The crypto.randomUUID() method is part of the Web Crypto API and ships in Chrome 92+, Firefox 95+, Safari 15.4+, and every evergreen Edge. It returns a secure v4 UUID string and is the only correct way to make one client-side.
const id = crypto.randomUUID();
console.log(id);
// "3f2a7b1c-8d4e-4b9c-a5f6-7e8d9c0b1a2f"
The function is synchronous, adds zero dependencies, and is backed by the same CSPRNG the browser uses for TLS. It's only available on secure contexts (HTTPS, localhost) — on an http:// page you'll hit TypeError: crypto.randomUUID is not a function.
In Node.js
Node 19 and later expose the same API on the global crypto object. For older Node you can import it explicitly:
// Node 19+
const id = crypto.randomUUID();
// Node 14.17+ compatible
const { randomUUID } = require("node:crypto");
const id2 = randomUUID();
Both paths call into OpenSSL's CSPRNG, so don't pull in the uuid package just for v4 — it's 12 kB on disk and slower than the built-in.
In Python
Python's standard library has shipped uuid since 2.5:
import uuid
new_id = uuid.uuid4()
print(new_id)
# UUID('a6c5f2e1-9b4d-4f8a-b3c2-8e1d7c5a4f0b')
print(str(new_id))
# 'a6c5f2e1-9b4d-4f8a-b3c2-8e1d7c5a4f0b'
uuid.uuid4() reads from /dev/urandom on Unix and CryptGenRandom on Windows, both cryptographically secure sources.
At the shell
Linux and macOS ship a kernel-level UUID source in /proc/sys/kernel/random/uuid (Linux) and uuidgen (both):
# macOS and most Linux distros
uuidgen
# F47AC10B-58CC-4372-A567-0E02B2C3D479
# Pure Linux
cat /proc/sys/kernel/random/uuid
Note that macOS's uuidgen returns uppercase by default. Pipe through tr '[:upper:]' '[:lower:]' if your API expects lowercase.
In a browser UI without writing code
If you need one or a hundred UUIDs to paste into a spreadsheet, staging data seed, or Postman request, the free iKit UUID Generator generates v4 UUIDs entirely in your browser with crypto.randomUUID(), copies to clipboard, and supports bulk export. Nothing ever leaves your tab.
When NOT to use UUID v4
This is the part most tutorials skip. UUID v4 is a general-purpose identifier, which means it's the right default for many things and the wrong default for three specific cases.
As a primary key in high-write OLTP tables
UUID v4 is fully random. When you insert 10,000 new rows per second, each row lands at a random position in the B-tree index, causing the database to split pages, dirty a fresh buffer, and flush more WAL than a sequential key would. On Postgres and MySQL with InnoDB, this fragmentation is measurable and painful past a few hundred million rows.
The fix isn't to avoid UUIDs — it's to use one that sorts by time. UUID v7 (from RFC 9562) encodes a 48-bit Unix millisecond timestamp in the first half:
0189f7d2-4a1c-7b8d-9e3f-2c4a6b8d0f1e
^^^^^^^^^^^^^ ^
timestamp ms version 7
ULID does the same thing with a different base-32 alphabet. Both keep uniqueness but add index locality, so inserts stay clustered at the tail of the index.
For session tokens, API keys, or password reset codes
A UUID v4 has 122 bits of entropy, which is plenty, but it's also widely recognized as "just a random ID" — meaning tools, logs, and error messages treat it as low-sensitivity and may log it in places where a real secret shouldn't go. Worse, developers sometimes reach for UUID v4 in browser code, not realizing that only crypto.randomUUID() is secure — Math.random()-based implementations are still everywhere on NPM.
For anything that acts as a credential, use a purpose-built tool like the iKit Password Generator to produce high-entropy random strings, and treat them with the same care as any other secret.
For user-facing IDs
f47ac10b-58cc-4372-a567-0e02b2c3d479 is unreadable, unwritable, and impossible to remember. Nobody wants a support ticket that starts with "my order ID is ef87…". If the identifier will ever be spoken, typed, or read aloud, use a shorter human-readable format — squad names, incrementing numbers, or a 6-character base-32 code. Keep the UUID internal.
UUID v4 vs UUID v7, ULID, and Nanoid
| Identifier | Size | Sortable by time | Collision space | Best for |
|---|---|---|---|---|
| UUID v4 | 128 bits | No | 2^122 | General-purpose random IDs |
| UUID v7 | 128 bits | Yes (ms) | 2^74 | Database primary keys (modern) |
| ULID | 128 bits | Yes (ms) | 2^80 | Database keys + lexical ordering |
| Nanoid (21) | ~126 bits | No | ~2^126 | Short URL-safe IDs (A-Za-z0-9_-) |
| Snowflake | 64 bits | Yes | Per-worker | Distributed systems with coordinator |
For new projects picking a primary key today, UUID v7 is the right default. Stick with v4 when you explicitly want to leak no timing information — for example, surrogate keys on a table that logs authentication attempts.
Common mistakes that break UUID v4
Even with crypto.randomUUID() available everywhere, three failure modes keep showing up in production code.
Using Math.random() instead of a CSPRNG
The oldest mistake. Math.random() is not cryptographically secure and may be seeded from the process's start time. A well-known 2015 bug in early versions of the uuid NPM package produced predictable UUIDs because of this. If you see code that looks like this, rewrite it:
// DO NOT USE — biased and predictable
function badUuid() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
const v = c === "x" ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
crypto.randomUUID() has been universally supported since 2022. There is no excuse for the above code in 2026.
Regenerating instead of storing
A common mistake in background jobs and retry logic is generating a new UUID on each attempt instead of passing the original along. This breaks idempotency — the downstream API can no longer deduplicate. Generate the UUID once, at the boundary where the job originates, and pass it through every retry.
Truncating UUID v4 to save space
"We only need 8 characters, so let's trim the UUID." At 8 hex characters you have 32 bits — a birthday collision becomes likely after only 65,000 values. If you need shorter, use a purpose-built short ID (Nanoid with a custom alphabet and length) instead of truncating a UUID.
Validating UUID v4 with a regex
To confirm a string is a well-formed v4, use the anchored pattern:
const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
UUID_V4.test("f47ac10b-58cc-4372-a567-0e02b2c3d479"); // true
UUID_V4.test("f47ac10b-58cc-1372-a567-0e02b2c3d479"); // false (version is 1)
UUID_V4.test("f47ac10b-58cc-4372-c567-0e02b2c3d479"); // false (variant is wrong)
This checks the version nibble (always 4) and the variant bits (first character must be 8, 9, a, or b). If you're parsing JSON payloads that contain UUIDs, the iKit JSON Decoder formats and validates nested structures so you can spot malformed IDs at a glance before the database rejects them.
Related on iKit
- Create strong passwords you'll actually remember — Both UUID v4 and a strong password lean on
crypto.getRandomValues()— and Math.random() is not safe for either. - Encode and decode Base64 with real examples — Encode a UUID as a 22-character URL-safe Base64 string when you need a shorter ID — same bytes, smaller footprint.
- Verify file integrity with MD5 and SHA-256 — Same
cryptoAPI, different output: random IDs on one side, deterministic fingerprints on the other.
Related posts
Apple Touch Icon Generator: Sizes, Specs & Setup (2026)
Apple touch icon generator that builds every iOS, iPadOS, and Safari pinned-tab size from one source — no upload, no sign-up, on-brand in 30 seconds.
Android Mipmap Generator: 5 Density Folders, One Click (2026)
Generate every Android mipmap density (mdpi to xxxhdpi) from one source PNG — no Android Studio, no upload, ready as a drop-in res/ folder in seconds.
Crop PDF Online: Trim White Margins (No Upload, 2026)
Trim white margins from any PDF in your browser — no upload, no watermark, no sign-up. Here is exactly how PDF cropping works in 2026.