The short answer: choose UUID v4 when you want a simple, widely supported random identifier and its random sort order is acceptable. Choose UUID v7 when chronological ordering and index locality matter. A UUID v4 has 122 random bits after the required version and variant bits. A UUID v7 has a 48-bit Unix timestamp in milliseconds followed by 74 bits available for randomness and optional monotonic schemes, with the version and variant between them. Neither version guarantees global uniqueness, and neither should be treated as an authorization token.
UUID v4 vs v7 at a glance
UUID version 4 and version 7 share the same outer container: 128 bits, the same RFC variant marker, and the familiar hexadecimal string form. The meaningful difference is how those bits are assigned. Version 4 deliberately reveals no creation time and gets collision resistance from random bits. Version 7 puts time in the most significant 48 bits so ordinary bytewise ordering is primarily chronological, then uses the remaining space to reduce collision risk and, in some implementations, improve ordering among values created during one clock tick.
| Property | UUID v4 | UUID v7 |
|---|---|---|
| RFC 9562 section | Section 5.4 | Section 5.7 |
| Total size | 128 bits | 128 bits |
| Defining input | Random or pseudorandom bits | Unix epoch milliseconds plus a remaining field |
| Random capacity | 122 bits after version and variant | Up to 74 bits when the remaining field is random |
| Natural RFC byte order | Random | Time-ordered across differing millisecond values |
| Same-millisecond order | Not meaningful | Not guaranteed by a random-tail generator |
| Time disclosure | No timestamp field | Approximate creation time is visible |
| Typical reason to choose it | Simplicity and broad runtime support | Chronological sortability and better insertion locality |
Neither row should be read as an absolute performance promise. RFC 9562 explains why time-ordered values can improve database-index behavior, but the result depends on the engine, UUID comparison rules, page layout, fill factor, workload, replication strategy, and whether the primary key is clustered. Benchmark the database configuration that will actually store the identifiers.
The shared 128-bit UUID format
The standard textual representation contains 32 hexadecimal digits separated into groups of 8, 4, 4, 4, and 12 characters:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
M = version nibble
N = first hexadecimal digit containing the variant bits
The full string is 36 characters including four hyphens. Hexadecimal digits may be uppercase or lowercase when parsed, although lowercase output is a useful normalization convention. For v4, the first digit in the third group is 4. For v7, it is 7. Under the RFC variant used by both versions, the first digit in the fourth group is normally 8, 9, a, or b because its two highest bits are 10.
A pattern match can confirm the text shape, version nibble, and variant nibble. It cannot prove that a value came from a compliant generator, that its random source was strong, that a v7 timestamp is truthful, or that the value is unique. Those are provenance and implementation questions, not properties encoded in the string.
Byte order matters at system boundaries. RFC 9562 specifies UUID fields in network byte order. Some Microsoft COM GUID binary representations have historical little-endian field behavior. Text round-trips often hide this difference, so verify the exact byte contract before moving a UUID between raw buffers, database drivers, COM APIs, and network protocols.
How UUID v4 works
A version 4 UUID is the random UUID. An implementation may start with 128 random or pseudorandom bits and overwrite the fixed fields, or generate exactly the unfixed bits and place them around those fields. Four bits identify version 4, and two bits identify the RFC variant. That leaves 122 random bits.
The standard does not require a central service, a machine address, or a timestamp. This makes v4 easy to generate independently in browsers, application servers, command-line tools, and offline clients. It also means the value has no useful chronological relationship to the next value. Sorting v4 identifiers sorts random values, not records by creation time.
The RFC says random-number generation should use a cryptographically secure pseudorandom number generator when one is available. That requirement is about reliable random UUID generation; it does not turn the resulting identifier into a secret. Use the platform UUID API or a well-maintained library rather than Math.random(), an ad hoc linear generator, or random bytes from an unclear source.
Version 4 is a strong default when compatibility is more important than time ordering, when IDs may be generated in many disconnected systems, or when exposing creation time would be undesirable. It is also useful when an older language or database has a trustworthy v4 primitive but no reviewed v7 implementation. The tradeoff is that random primary-key inserts may touch widely separated index pages.
How UUID v7 works
Version 7 is the time-ordered UUID recommended by RFC 9562 for new time-based use cases. Its first 48 bits contain an unsigned Unix epoch timestamp in milliseconds, with leap seconds excluded. The next four bits are the version value 0111. A 12-bit field called rand_a follows, then the two-bit RFC variant, then a 62-bit rand_b field.
| Bit range | Width | UUID v7 field |
|---|---|---|
| 0–47 | 48 bits | unix_ts_ms: Unix epoch milliseconds |
| 48–51 | 4 bits | ver: the value 7 |
| 52–63 | 12 bits | rand_a: random data or optional monotonic structure |
| 64–65 | 2 bits | var: the RFC variant value |
| 66–127 | 62 bits | rand_b: random data or optional monotonic structure |
After subtracting the timestamp, version, and variant, 74 bits remain. The simplest compliant design fills all 74 with pseudorandom data. RFC 9562 also permits optional sub-millisecond timestamp fractions and counters, in a specified order, to create stronger monotonic behavior. Those options are generator policies; they are not extra fields that every v7 parser can assume are present.
The time prefix means UUIDs from later millisecond timestamps sort after earlier ones when compared in RFC byte order. But random-tail v7 values created in the same millisecond are not guaranteed to follow generation order. Their equal timestamp prefixes leave random suffixes to decide the comparison. A generator that promises strict or monotonic ordering needs documented state, counter, clock rollback, overflow, and concurrency behavior.
System clocks can repeat or move backward. RFC 9562 describes approaches for monotonicity, but it does not make wall clocks infallible. If ordering is a business invariant, do not infer it solely from arbitrary v7 strings. Store an authoritative creation timestamp, transaction sequence, or log position as a separate field and define how ties and clock anomalies are handled.
Standard requirements versus implementation choices
Many UUID comparisons accidentally turn a library behavior into a claim about the standard. Keep the two layers separate:
| RFC-defined property | Implementation choice |
|---|---|
| A UUID occupies 128 bits. | Whether an API exposes bytes, two integers, a native UUID object, or a string. |
| v4 fixes version and variant, leaving 122 random bits. | Which approved CSPRNG, entropy cache, or operating-system source supplies them. |
| v7 starts with a 48-bit Unix-millisecond timestamp. | How the clock is sampled and what happens if it moves backward. |
| v7 has 74 remaining bits around the variant marker. | Whether those bits are all random or include a fraction and/or counter. |
| RFC byte order supports v7 time sorting across timestamps. | How a particular database or language compares its native UUID values. |
| Text accepts hexadecimal UUID notation. | Whether an application emits lowercase, accepts braces, or tolerates alternate formats. |
A library can offer guarantees beyond the base format, such as process-local monotonic output. Record the library name and version when relying on one. Test generation under concurrent threads, multiple processes, clock rollback, and sustained same-millisecond load. A parser should still treat unfamiliar v7 suffix bits as opaque instead of assuming another generator used the same counter design.
Database indexes, locality, and storage
Random v4 inserts tend to distribute new keys throughout the index keyspace. With an ordered tree index, that can lead to page splits, cache churn, and less predictable write locality. Because v7 starts with time, newly generated values usually land near other recent values. This is the principal database motivation for v7.
“Usually” is important. Values generated during the same millisecond may have random order, imported historical rows can target older ranges, and a clock that moves backward can produce an earlier prefix. Database-specific comparison is equally important. PostgreSQL’s native uuid ordering works with the UUID value, while Microsoft SQL Server’s uniqueidentifier comparison rules and some GUID byte conversions should be tested rather than assumed to match RFC lexical order.
Use a database’s native UUID type when it has one. RFC 9562 recommends storing UUIDs as the binary 128-bit value where feasible instead of a textual representation. Native storage avoids spending space on 36-character formatting and lets the database validate the basic type. Text may still be appropriate in systems without a native type, in human-facing interchange, or where a schema contract explicitly requires it.
Do not remove a separate created_at column merely because v7 contains time. A creation timestamp can have declared precision, time-zone semantics, indexing policy, correction rules, and application meaning. The embedded v7 timestamp is useful for coarse ordering and diagnostics, but it is not a complete audit record. It may reflect client clock time rather than database commit time.
For a migration, avoid rewriting every existing v4 key only to make it look like v7. Existing identifiers remain valid UUIDs. A common approach is to generate v7 for new rows, keep the column type unchanged, and let both versions coexist. Audit foreign keys, serializers, caches, partitioning, sharding, and code that incorrectly assumes every UUID has version 4 before switching the default.
Is a GUID different from a UUID?
In ordinary developer usage, GUID is Microsoft’s name for the same broad family of 128-bit identifiers, and RFC 9562 notes that UUIDs are also known as GUIDs. A .NET Guid can hold an RFC variant version 4 or version 7 value. The word “GUID” by itself does not identify an algorithm or version.
The important differences appear in APIs, binary serialization, and database ordering—not in a magical extra uniqueness property. A tool labeled “GUID generator” often generates UUID v4. A UUID v4 generator is therefore usually the appropriate choice when a system simply asks for a new random GUID. When interoperability matters, specify the version, RFC variant, textual form, and binary byte order instead of relying on the label alone. The canonical LiveParse UUID Generator covers random UUID generation, including the use case commonly described as GUID generation.
Validation, version detection, and opacity
A useful validator answers separate questions. Does the input have an accepted textual form? Does it decode to 128 bits? Does it use the RFC variant? Which version nibble does it contain? Is that version allowed by this field’s contract? The LiveParse UUID Validator can help inspect those structural properties.
v4: ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
v7: ^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
Those expressions intentionally accept only lowercase standard text. Add case-insensitive matching if the protocol accepts uppercase. Do not add optional braces, urn:uuid:, missing hyphens, or whitespace unless the surrounding contract permits those forms. A generic UUID parser may accept more representations than a public API should.
Applications should otherwise treat UUIDs as opaque identifiers. It can be legitimate for version-aware infrastructure to extract a v7 timestamp, but ordinary domain logic should not split strings, attach meaning to suffixes, or assume that a visible prefix identifies a tenant or machine. The format can evolve, and optional v7 suffix construction is deliberately generator-specific.
Security and privacy limits
A UUID is an identifier, not proof of permission. RFC 9562 warns against assuming UUIDs are hard to guess and says they must not be used as security capabilities. Knowing a record’s UUID must not be the only condition for reading, modifying, or deleting it. Apply authentication and authorization independently.
Version 4 has a large random field, but its formatted value still provides no integrity, signature, expiry, audience restriction, or revocation mechanism. A weak or compromised random source can further reduce unpredictability. Version 7 exposes an approximate creation time and narrows the search space to values associated with that time window; that is intentional metadata, not a vulnerability that can be fixed while preserving the format.
For password-reset links, session credentials, API secrets, email-verification links, and object capabilities, use a purpose-built high-entropy token design with the necessary lifecycle and server-side controls. If a UUID is included inside a signed token or authenticated message, the surrounding cryptography provides authenticity—the UUID itself does not.
Neither v4 nor v7 includes tamper detection. Changing one hexadecimal digit usually produces another structurally valid-looking identifier. Never use format validation as evidence that a request came from a trusted party. Avoid placing sensitive UUID-associated data in logs or URLs merely because the identifier looks random, and review the LiveParse privacy approach before handling production values.
JavaScript and Node.js examples
In a secure browser context, crypto.randomUUID() generates a standards-form version 4 UUID. Browsers do not currently expose a matching standardized randomUUIDv7() Web API. The small example below demonstrates the RFC v7 layout using Date.now() and crypto.getRandomValues(). It uses an all-random 74-bit tail, so it is not monotonic for calls within the same millisecond.
const id4 = crypto.randomUUID();
function uuidv7RandomTail() {
const bytes = crypto.getRandomValues(new Uint8Array(16));
const ms = BigInt(Date.now());
bytes[0] = Number((ms >> 40n) & 0xffn);
bytes[1] = Number((ms >> 32n) & 0xffn);
bytes[2] = Number((ms >> 24n) & 0xffn);
bytes[3] = Number((ms >> 16n) & 0xffn);
bytes[4] = Number((ms >> 8n) & 0xffn);
bytes[5] = Number(ms & 0xffn);
bytes[6] = (bytes[6] & 0x0f) | 0x70;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const hex = [...bytes].map(byte =>
byte.toString(16).padStart(2, "0")
).join("");
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${
hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}
const id7 = uuidv7RandomTail();
This is educational code, not a promise of process-wide monotonicity or a substitute for a reviewed library policy. A production generator may need concurrency coordination and clock rollback handling. Use the UUID v7 Generator to inspect the layout, and choose a maintained RFC 9562 implementation when stronger operational guarantees matter.
Node.js has different support by release. randomUUID() has generated v4 UUIDs since Node 14.17.0 and 15.6.0. randomUUIDv7() is available in Node 24.16.0 and Node 26.1.0 or newer releases on those lines. The Node documentation explicitly notes that its clock is not guaranteed to be monotonic, so increasing output is not an unconditional guarantee.
import { randomUUID, randomUUIDv7 } from "node:crypto";
const id4 = randomUUID();
const id7 = randomUUIDv7();
console.log({ id4, id7 });
Do not paste randomUUIDv7() into an older Node runtime and assume a polyfill exists. Check the exact runtime version: Node 24 needs 24.16.0 or newer and Node 26 needs 26.1.0 or newer. For unsupported releases, use a maintained library that explicitly implements RFC 9562 v7, or generate the value in another trusted system component.
Python example
Python has long provided uuid.uuid4(). Python 3.14 added uuid.uuid7(), so the standard-library v7 call is version-dependent. Python documents uuid4() as cryptographically secure and exposes the embedded millisecond timestamp of a v7 object through its time attribute.
import uuid
id4 = uuid.uuid4()
id7 = uuid.uuid7()
assert id4.version == 4
assert id7.version == 7
created_ms = id7.time
print(id4, id7, created_ms)
Python 3.13 and earlier do not have uuid.uuid7() in the standard library. Select a vetted RFC 9562 package and pin its version rather than inventing a method name or silently falling back to v4. Also confirm whether the chosen package promises monotonic output, random-tail output, or merely format compliance.
PostgreSQL example
PostgreSQL 18 includes built-in uuidv4() and uuidv7() generation functions. gen_random_uuid() remains an alias-style route to a v4 value. PostgreSQL can also extract an RFC UUID version and extract the timestamp from v1 or v7 values.
CREATE TABLE events (
id uuid PRIMARY KEY DEFAULT uuidv7(),
created_at timestamptz NOT NULL DEFAULT now(),
payload jsonb NOT NULL
);
SELECT uuidv4() AS random_id,
uuidv7() AS time_ordered_id;
SELECT uuid_extract_version(id),
uuid_extract_timestamp(id)
FROM events;
The built-in uuidv7() call shown here is specific to PostgreSQL 18. On PostgreSQL 17 and earlier, generate v7 in the application or deliberately choose an audited extension; do not assume the function exists. Keep created_at when it has application or audit meaning. PostgreSQL also offers an optional timestamp-shift argument, but changing the embedded time is a specialized implementation feature, not a way to make identifiers private.
Java and C# examples
Java’s UUID.randomUUID() generates v4 using a cryptographically strong pseudorandom number generator. Java SE 26 added UUID.ofEpochMillis(long) for v7. The caller supplies the timestamp, and Java fills the remaining 74 bits with cryptographically strong random data. Callers that require monotonic timestamps must enforce that property themselves.
import java.util.UUID;
UUID id4 = UUID.randomUUID();
UUID id7 = UUID.ofEpochMillis(System.currentTimeMillis());
System.out.println(id4 + " version=" + id4.version());
System.out.println(id7 + " version=" + id7.version());
Java 25 and earlier do not provide ofEpochMillis. Use a maintained RFC 9562 library on those releases. Note also that the older UUID.timestamp() instance method is defined for v1 and is not the matching v7 extractor; do not call it on the v7 object shown above.
In .NET, Guid.NewGuid() creates a version 4 value. .NET 9 added Guid.CreateVersion7(), which uses UTC time and random data for the v7 random fields. The return type is still Guid because that is the platform’s long-standing type name.
Guid id4 = Guid.NewGuid();
Guid id7 = Guid.CreateVersion7();
Console.WriteLine($"v4: {id4}");
Console.WriteLine($"v7: {id7}");
Earlier .NET versions do not have CreateVersion7(). Use a reviewed library or keep generating v4 until the runtime upgrade. When serializing raw Guid bytes, verify the receiving system’s expected field order; the textual form is often the safer interoperability contract.
How to choose and migrate
- Choose v4 for the broadest built-in support, simple decentralized generation, and no embedded creation time.
- Choose v7 when time-oriented sorting and database insertion locality are useful and revealing millisecond time is acceptable.
- Keep a real timestamp column when the application needs authoritative creation, update, event, or commit time.
- Document same-tick behavior if callers expect monotonic output; “v7” alone does not make random tails sequential.
- Use a native UUID database type where available, and test the engine’s actual comparison and clustering behavior.
- Allow both versions during migration unless a field has a genuine reason to reject older identifiers.
- Pin runtime and library requirements because v7 standard-library APIs arrived in different versions of Node, Python, Java, .NET, and PostgreSQL.
- Keep access control separate because neither UUID version is a security token.
For a greenfield service using a current runtime and an ordered primary index, v7 is often the more operationally convenient default. For an integration with mixed clients or older platforms, v4 may be the safer compatibility choice. The right answer is the smallest set of guarantees the system truly needs—not the newest version number.
Generate or inspect a UUID locally. Use the LiveParse UUID Generator for random UUIDs, the UUID v7 Generator for time-ordered values, or the UUID Validator to check format, variant, and version.
Open the UUID v7 GeneratorFrequently asked questions
Is UUID v7 better than UUID v4?
Not universally. Version 7 is usually better when chronological ordering and index locality matter. Version 4 is simpler, more widely available in older runtimes, and does not reveal a timestamp. Both fit in the same 128-bit UUID type and both can be generated without a central allocator.
Is UUID v7 guaranteed to be unique?
No UUID version can guarantee global uniqueness without shared knowledge of every generated value. A compliant generator makes collisions practically unlikely through its timestamp and remaining bits, but applications that require an absolute uniqueness invariant must still enforce it, typically with a unique constraint and a defined retry policy.
Is UUID v7 always monotonic?
No. Values with different increasing millisecond timestamps have chronological order in RFC byte order. Values produced within the same millisecond by a generator that fills all 74 remaining bits randomly are not guaranteed to preserve call order. Clock rollback can also disturb order. A stronger claim requires a specific generator’s documented counter and clock policy.
How many random bits are in UUID v4 and v7?
UUID v4 has 122 random bits because six of the 128 bits encode the version and variant. UUID v7 has 74 bits remaining after its 48-bit timestamp, four-bit version, and two-bit variant. A v7 implementation may use all 74 randomly or allocate part of that space to optional sub-millisecond or counter logic.
Can UUID v4 and UUID v7 share one database column?
Yes. They are both 128-bit UUIDs and can coexist in a native UUID column. Check application validators that may have been hard-coded for the v4 nibble, and remember that old random rows will not become time-sortable. A separate creation-time column remains useful.
Can I extract the creation time from UUID v7?
You can extract its 48-bit Unix-millisecond field, but describe it as the timestamp embedded by the generator. It may come from a client clock, may be shifted by a particular API, and may not equal database commit or business-event time. It should not replace an authoritative audit timestamp.
Does GUID mean UUID v4?
No. GUID is a common platform term for a 128-bit identifier, not a version. Many “new GUID” APIs historically produced v4, while modern .NET can also return an RFC 9562 v7 value in a Guid. Inspect or require the version when the distinction matters.
Can I use a UUID as an API key or reset token?
Do not rely on a UUID as a security capability. UUIDs provide identification, not authorization, integrity, expiry, or revocation. Use a purpose-built token system and enforce access control even if resource identifiers are difficult to guess.
Should UUIDs be stored as text or binary?
Prefer the database’s native UUID type or a 128-bit binary representation when practical. Text is convenient for logs and interchange but includes formatting overhead and may invite inconsistent casing or punctuation. Define byte order whenever raw binary crosses a system boundary.
Primary references
- RFC 9562: Universally Unique IDentifiers — the current UUID layouts, generation requirements, monotonicity methods, storage guidance, and security considerations.
- MDN:
Crypto.randomUUID()— browser v4 generation and secure-context availability. - Node.js Crypto documentation — version-specific
randomUUID()andrandomUUIDv7()behavior. - Python
uuiddocumentation — v4 and Python 3.14 v7 APIs. - PostgreSQL 18 UUID functions — built-in generators and extraction functions.
- Java SE 26
UUID— v4 generation andofEpochMillisv7 construction. - .NET 9
Guid.CreateVersion7— RFC 9562 v7 generation in C# and other .NET languages.