GUID generator

GUIDs in every .NET format, including the braced registry form, with the mixed-endian byte order problem explained.

G

GUID and UUID are the same 128-bit value

There is no technical difference. GUID is the Microsoft name for a UUID, it is the same 128 bits, it follows the same version and variant rules, and Guid.NewGuid() returns a version 4 UUID as defined by RFC 9562. A GUID generated by Windows will parse as a UUID in Python, and the reverse is also true.

What differs is convention and serialisation. Microsoft tooling displays GUIDs in uppercase, wraps them in braces in the registry and in COM, and, in one place that catches people out, writes the bytes in a different order than the RFC does. The first two are cosmetic. The third is not, and it is the substance of this page.

The mixed-endian byte order trap

The .NET Guid type is a struct of four fields: a 32-bit integer, two 16-bit integers, and eight bytes. On a little-endian machine the first three of those are stored in native order, so a straight memory dump writes them backwards relative to the text form. RFC 9562 defines the value as big-endian throughout. COM defined the struct in 1993 and .NET inherited it, so the two disagree and have always disagreed.

Microsoft documents the effect with a worked example. The GUID 35918bc9-196d-40ea-... serialises through the default byte array as C9 8B 91 35 6D 19 EA 40, with the first four bytes reversed, then the next two reversed, then the next two reversed, and the final eight left in order. Feed that array to any non-Microsoft parser and you get a different GUID back: 35918bc9 becomes c98b9135. The value is not corrupted, it is transposed, which is worse, because it still looks like a perfectly valid GUID.

This bites whenever a GUID crosses a boundary as bytes rather than as text. A .NET service writing Guid.ToByteArray() into a binary protocol, a Kafka key, a BINARY(16) column, or a file header, read by a Java or Go or Python consumer, produces exactly this. The fix is to be explicit on both sides. .NET 8 added ToByteArray(bigEndian: true) and a matching Guid(byte[], bigEndian) constructor; use them, or convert to the canonical text form at the boundary and accept the extra twenty bytes.

If the same GUID renders differently on two sides of a system, do not assume corruption. Compare the byte arrays: if the first three groups are individually reversed and the last eight bytes match, it is the endianness and not the data.

Format specifiers, and where each one is used

The .NET Guid.ToString() overload takes five specifiers. Most GUID tools emit only the default. The braced form is the one Windows itself uses in the registry, in COM class identifiers, in .reg files and in a number of installer formats, and it is the one people most often come to a generator looking for.

RFC 9562 relaxed the casing rule that RFC 4122 had imposed. Lowercase output is no longer mandatory and mixed case is explicitly permitted, though every parser must accept all three. Windows tooling conventionally uppercases; the RFC world conventionally lowercases. If you compare GUIDs as strings anywhere, normalise the case first, or better, compare them as 128-bit values.

The five .NET format specifiers, plus the URN form
SpecifierShapeLengthTypically used for
D (default)8-4-4-4-12 with hyphens36Everything, unless something demands otherwise
N32 hex digits, no hyphens32Compact storage, cache keys, file names
B{8-4-4-4-12}38Registry keys, COM CLSIDs, .reg files
P(8-4-4-4-12)38Some legacy Microsoft tooling
XNested hex struct literal68C and C++ source, IDL, header files
urn:uuid:urn:uuid: then the D form45XML, RDF, anything expecting a URN

SQL Server: NEWID, NEWSEQUENTIALID, and why v7 does not help

NEWID() returns a random GUID and is evaluated per row. Microsoft documents it as RFC 4122 compliant without naming a version, so treat the version nibble as an implementation detail rather than a contract. Used as a clustered primary key it has the same problem random keys have everywhere: inserts land at arbitrary points in the index, pages split, and the index fragments. The standard advice is to switch to a time-ordered UUID, and on PostgreSQL or MySQL InnoDB that advice is correct.

It does not transfer to SQL Server. Microsoft documents that ordering for the uniqueidentifier type is not implemented by comparing the bit patterns of the values, which is to say the sort order of the column is not the numeric order of the 128 bits. A version 7 GUID stored in a uniqueidentifier column therefore does not cluster by creation time, and the index locality that makes v7 worth choosing does not appear. This is a genuine exception to a piece of advice that is otherwise sound, and it is why you should measure rather than assume.

NEWSEQUENTIALID() is the SQL Server answer to the same problem. It produces values that increase in the order that column sorts in, which restores insert locality. It comes with two constraints. It is only valid as a DEFAULT expression on a uniqueidentifier column, so you cannot call it in a SELECT or an INSERT list. And its output is predictable by design: given one value, the next is guessable, so it must never be used for anything that is exposed publicly or that acts as a capability. Keep it internal and generate a separate v4 GUID for anything a client sees.

The other option is to store the GUID as BINARY(16) in RFC byte order and index that, which gives you ordinary byte comparison and portability at the cost of losing the uniqueidentifier type and its tooling. Which of the three is right depends on whether you need locality, unguessability or interoperability most.

Doing this in your own code

C# / .NET
Guid g = Guid.NewGuid();          // version 4
Guid v7 = Guid.CreateVersion7();  // .NET 9 and later
Guid.Empty;                       // 00000000-0000-0000-0000-000000000000

g.ToString();      // d, the default: 8-4-4-4-12, lowercase
g.ToString("N");   // 32 hex digits, no hyphens
g.ToString("B");   // {8-4-4-4-12}  <- registry and COM form
g.ToString("P");   // (8-4-4-4-12)
g.ToString("X");   // {0x...,0x...,0x...,{0x..,...}}

// Byte order. The default is mixed-endian and has been since COM.
g.ToByteArray();                  // first three fields little-endian
g.ToByteArray(bigEndian: true);   // RFC 9562 order, .NET 8 and later

// The matching parse, also .NET 8 and later. The overload takes a
// ReadOnlySpan<byte>; there is no Guid(byte[], bool), but a byte[]
// converts to the span implicitly.
new Guid(bytes, bigEndian: true);

If a GUID crosses a process boundary as bytes rather than as text, one side using the default overload and the other reading RFC order is the bug you will spend an afternoon on.

PowerShell
New-Guid                       # 8-4-4-4-12, lowercase
[guid]::NewGuid().ToString("B")   # braced, for a .reg file
[guid]::NewGuid().Guid.ToUpper()  # uppercase, the Windows tooling convention

# Round trip through bytes and back, so the endianness is visible
$g = [guid]::NewGuid()
[BitConverter]::ToString($g.ToByteArray())
T-SQL (SQL Server)
SELECT NEWID();   -- a new uniqueidentifier per row. Microsoft documents
                  -- NEWID() as RFC 4122 compliant but names no version.

-- NEWSEQUENTIALID is only legal as a DEFAULT on a uniqueidentifier column.
CREATE TABLE orders (
  id       uniqueidentifier NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
  created  datetime2 NOT NULL DEFAULT SYSUTCDATETIME()
);

-- uniqueidentifier ordering is NOT byte order. A time-ordered v7 value
-- does not sort or cluster the way it does on PostgreSQL or InnoDB.
SELECT id FROM orders ORDER BY id;
SELECT CAST(id AS binary(16)) FROM orders;  -- compare the two orders

Microsoft documents that uniqueidentifier ordering is not implemented by comparing the bit patterns. That single sentence is why the generic "use v7 for your primary key" advice does not transfer to SQL Server.

C / Win32
#include <objbase.h>

GUID g;
CoCreateGuid(&g);   // Windows 2000 and later. It calls UuidCreate, which
                    // Microsoft documents as producing a value that cannot
                    // be traced to the machine's ethernet address. The docs
                    // name no UUID version, so do not depend on one.

// The struct is the mixed-endian layout, in the open:
//   unsigned long  Data1;    // 4 bytes, native order
//   unsigned short Data2;    // 2 bytes, native order
//   unsigned short Data3;    // 2 bytes, native order
//   unsigned char  Data4[8]; // 8 bytes, byte order as written

wchar_t buf[39];
StringFromGUID2(&g, buf, 39);   // {8-4-4-4-12}, uppercase, braced

Questions

Is a GUID the same as a UUID?

Yes, it is the same 128-bit value under a different name. Guid.NewGuid() returns a version 4 UUID exactly as RFC 9562 defines it. The only real difference is serialisation: .NET writes the first three fields in little-endian order by default, which is why a GUID can look different after a round trip through bytes.

Why does my GUID look different after saving and loading it?

Almost certainly the mixed-endian byte order. Guid.ToByteArray() reverses the first four bytes, then the next two, then the next two, and leaves the final eight alone. A reader that expects RFC order will reassemble those as a different, still valid-looking GUID. Use ToByteArray(bigEndian: true) on .NET 8 and later, or pass the value as text.

Does .NET support UUIDv7?

Yes, from .NET 9, as Guid.CreateVersion7(). There is also an overload taking a DateTimeOffset. On earlier versions you need a library or your own implementation.

How do I get a GUID with braces?

Select the braced format on this page, or call ToString("B") in .NET. That is the form the Windows registry and COM use. Most GUID generators do not offer it.

Should GUIDs be uppercase or lowercase?

Either is valid. RFC 4122 mandated lowercase output; RFC 9562 permits upper, lower or mixed while requiring parsers to accept all of them. Windows tooling conventionally uses uppercase. The practical rule is to normalise before any string comparison, since the two forms are equal as values and unequal as text.

Is NEWSEQUENTIALID safe to expose?

No. Its values are predictable from one another by design, which is the whole point of it, so anyone holding one identifier can guess its neighbours. Use it for internal clustering only, and generate a separate random GUID for anything that reaches a client.

Can I use a GUID as a security token?

A version 4 GUID from a proper CSPRNG carries 122 random bits, which is enough. A version 7 GUID is not suitable, because 48 of its bits are a readable timestamp. NEWSEQUENTIALID output is not suitable at all.

Related generators