Quick decision

I need…Use
A database primary keyv7 — time-ordered, index-friendly
A URL to share with usersv4 — no timestamp, no leakage
An event or log correlation IDv7 — creation time is useful
A password-reset or invite tokenv4 — opaque, nothing inferred
Both storage and public exposurev7 primary key + v4 public handle

The short answer

Use UUID v7 for anything you control the storage of — database primary keys, event IDs, log correlation IDs, message keys. Use UUID v4 for anything a user or third party sees where creation time or creation rate is sensitive — share links, password-reset tokens, invitation codes, public object IDs.

Both are 128 bits, both are defined by RFC 9562, and both have collision probabilities low enough to ignore in practice. The decision is not about uniqueness. It is about whether embedding a readable timestamp in the identifier helps you or hurts you.

What actually differs: the bits

A v4 UUID is 122 bits of random data, with 4 bits fixed as the version and 2 as the variant. Nothing about it is derived from the machine, the clock, or the sequence of previous values.

A v7 UUID spends its first 48 bits on a big-endian Unix millisecond timestamp. The version and variant nibbles sit in the same positions as v4, and the remaining 74 bits carry randomness — with the option, taken by this site’s generator, of using 12 of them as a per-millisecond counter so that values minted inside the same millisecond still sort correctly (RFC 9562 §6.2, Method 1).

Because the timestamp occupies the most significant bits and UUIDs are written as big-endian hex, lexicographic string order matches chronological order for v7. Sorting v7 UUIDs as text, as bytes, or as native uuid values all give the same sequence. That single property is the source of every practical difference below.

Why the difference shows up in a database

Most relational databases store the primary key in a B-tree. Where a new key lands in that tree determines how much work the insert costs.

With v4, every insert targets a uniformly random position in the key space. As the table outgrows memory, each insert tends to touch a different leaf page, and each of those pages must be read in, modified, and written back. Pages fill unevenly and split, the tree carries more free space than it needs, and the working set that has to stay cached to keep inserts fast is effectively the whole index.

With v7, consecutive inserts share a timestamp prefix, so they land in the same handful of leaf pages. Those pages stay hot in cache, they fill up before the insert point moves on, and the index behaves much like one keyed on an auto-incrementing integer — without needing a central sequence, and while still being safe to generate on any client.

The trade-offs that come with that:

PostgreSQL 18 ships a native uuidv7() function. Earlier versions can store v7 values generated anywhere — the type is just 128 bits.

When v4 is the right answer

The timestamp that makes v7 useful internally is a disclosure when the identifier is public:

None of these are reasons to avoid v7 in your own tables. They are reasons to keep the public-facing identifier separate. A common pattern is a v7 primary key for storage plus a v4 (or another opaque token) as the externally shared handle.

Use v4 outright for password-reset links, invitation codes, unsubscribe links, and API keys — with the caveat that a UUID is an identifier, not a secret. It has no authentication properties on its own, and 122 bits of entropy is a floor, not an authorisation check.

Migrating from v4 to v7

Because both versions share a type and a format, the change is usually confined to the code that mints new values:

  1. Switch the generator. Point new inserts at a v7 implementation. Note that JavaScript’s built-in crypto.randomUUID() produces v4 only — v7 needs a library or a short implementation of your own.
  2. Leave existing rows alone. Old v4 values stay valid and keep resolving. The column accepts both.
  3. Do not sort mixed data by ID and expect chronology. Rows written before the switch have random keys; only the v7 ones carry order. Keep using created_at for anything spanning the cutover.
  4. Rebuild the index if the table is large and old. An index fragmented by years of random inserts does not repack itself; REINDEX reclaims that space once new inserts are sequential.

There is no need for a backfill. Rewriting historical IDs means rewriting every foreign key that references them, which is a large amount of risk in exchange for a cosmetic gain.

Choosing, in one pass

Ask whether the identifier is stored or shown:

If you are still unsure, v7 is the safer default for new work in 2026: the failure mode of v4 (index fragmentation) grows silently with table size and is awkward to fix later, while the failure mode of v7 (timestamp disclosure) is visible at design time and is solved by not putting the key in a URL.

48-bit ms timestamp v7 12-bit 10 62-bit random
UUID v7 layout: a 48-bit timestamp, the 4-bit version, a 12-bit counter / random field (used here as a monotonic counter), the 2-bit variant, and 62 bits of randomness.

When to use which

Use caseRecommendedWhy
Database primary keyv7Time-ordered inserts keep B-tree indexes healthy
Session tokens, share links, reset URLsv4Creation time must not leak; pure randomness
Event / log correlation IDsv7Natural time sort aids debugging
Public-facing opaque IDsv4No ordering information exposed
Offline / distributed generationeitherBoth need no central coordinator

Frequently asked questions

Should I use v4 or v7?

Use v7 for database primary keys (time-sortable, index-friendly) and v4 for anything where creation order could leak information, like tokens or share links.

Can I use UUID v7 in PostgreSQL?

Yes. PostgreSQL 18 ships a native uuidv7() function; earlier versions can store any UUID this tool generates in a uuid column.

Is UUID v7 better than UUID v4?

Neither is strictly better. v7 is the better default for database primary keys because its time-ordered prefix keeps B-tree inserts localised. v4 is the better choice for anything externally visible where the creation time or the rate of creation should stay private.

Does UUID v7 leak information?

Yes — the first 48 bits are a Unix millisecond timestamp, so anyone holding a v7 UUID can read when it was created to the millisecond. Comparing two v7 UUIDs also reveals how far apart the records were created, which can expose sign-up or transaction volume.

Can I mix UUID v4 and UUID v7 in the same column?

Yes. Both are 128-bit values with the same textual format, so a uuid column stores either. The version nibble tells them apart, and existing v4 rows keep working after you switch new inserts to v7.