Skip to content
UUID Generator
Blog 02

UUID vs GUID: the same 128 bits with different habits

Updated October 12, 2026 · Reading time: 6 min · 7 sections

A .NET service writes a GUID as 16 bytes, a Python job reads those bytes back, and the ID it prints matches nothing in the database. In the UUID vs GUID question the value is never the problem; the habits wrapped around it are, and byte order is the habit that bites hardest.

A GUID is a UUID

GUID, globally unique identifier, is the name Microsoft uses in COM, Windows and .NET for the 128-bit identifier that RFC 9562 defines as a UUID. Same size, same 8-4-4-4-12 text form, same version and variant fields. Guid.NewGuid() returns a random version 4 value with the RFC variant, and you can paste it into a PostgreSQL uuid column unchanged.

Three things do differ in practice: how the value is spelled, an old variant that some legacy GUIDs still carry, and how the 16 bytes are laid out in memory and on disk.

Spelling habits: capitals and braces

  • Windows registry and COM write GUIDs in upper case inside braces: {6BA7B810-9DAD-11D1-80B4-00C04FD430C8}.
  • SQL Server converts a uniqueidentifier to text in upper case, without braces.
  • .NET Guid.ToString() gives lower case with hyphens (format "D"); ToString("B") adds braces, ToString("N") drops the hyphens, ToString("P") uses parentheses.
  • RFC 9562 says to output lower case and accept any case on input. PostgreSQL prints lower case and also accepts upper case and braces.

So the same identifier travels as several strings. Code that compares those strings byte by byte is where "the IDs don't match" tickets come from.

The variant digit: 8 to b versus c and d

The top bits of the 17th hex digit say which layout the rest of the value follows. Modern GUIDs use the RFC variant, but older Microsoft identifiers use their own:

Variant by the 17th hex digit
17th digitVariantWhere you see it
0–7NCS, backward compatibilityObsolete systems; also the Nil UUID
8, 9, a, bRFC 9562 (OSF DCE)Everything modern, including Guid.NewGuid()
c, dMicrosoft, legacyOld COM and DCOM identifiers
e, fReservedFuture use; the Max UUID

The COM interface ID of IUnknown, 00000000-0000-0000-C000-000000000046, is a classic example: its 17th digit is C, so it is a Microsoft-variant GUID, and its version nibble means nothing. The Validate & decode tab of the UUID generator reports the variant for such values and does not invent a version for them.

Byte order: the swap that breaks round trips

Microsoft defines a GUID as a struct: a 32-bit integer, two 16-bit integers and 8 raw bytes. On little-endian machines those three integers are stored least significant byte first. That is why .NET Guid.ToByteArray() and the binary form of a SQL Server uniqueidentifier reverse the first three groups and keep the last two as written. RFC 9562 stores all 16 bytes in the order you read them.

Bytes of 00112233-4455-6677-8899-aabbccddeeff
LayoutBytes in storage order
RFC 9562 (big-endian)00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff
Microsoft (mixed-endian)33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff

You can check this in Python: uuid.UUID('00112233-4455-6677-8899-aabbccddeeff').bytes_le returns the Microsoft order, and uuid.UUID(bytes_le=data) reads it back. Reading Microsoft bytes as RFC bytes gives 33221100-5544-7766-8899-aabbccddeeff: a different, perfectly valid-looking ID.

The tell-tale sign is the version digit. A random v4 GUID swapped this way has its 4 pushed to the 15th position, so the decoder reports some unexpected version. When IDs coming out of .NET suddenly decode as version 9 or b, look for a byte swap first.

The fix is to exchange text or RFC-order bytes between systems. In .NET 8 and later, id.ToByteArray(bigEndian: true) and new Guid(bytes, bigEndian: true) give you the RFC order; on older versions, pass the string.

SQL Server sorts by the last group first

A known behaviour that surprises people: when SQL Server compares two uniqueidentifier values it starts with the last group of 12 hex digits, then the fourth group, and works back towards the first. ORDER BY id therefore does not match the alphabetical order of the strings.

This matters for time-ordered keys. A v7 from Guid.CreateVersion7() carries its timestamp in the first group, the part SQL Server compares last, so it does not append neatly to a clustered index there. NEWSEQUENTIALID() produces values tailored to SQL Server's own order instead. If you want ordered inserts on SQL Server, either use that function as the column default or cluster on a sequential column and keep the GUID as a unique nonclustered key. The UUID v4 vs v7 guide covers why ordering matters for indexes.

One identifier, many spellings

Here is the DNS namespace UUID from RFC 9562 in each form you are likely to meet. Every row is the same 128-bit value.

6ba7b810-9dad-11d1-80b4-00c04fd430c8 in common formats
FormatExampleWhere it shows up
Canonical lower case6ba7b810-9dad-11d1-80b4-00c04fd430c8RFC output, PostgreSQL, Python, .NET ToString()
Upper case6BA7B810-9DAD-11D1-80B4-00C04FD430C8SQL Server, Windows tools
Braces{6BA7B810-9DAD-11D1-80B4-00C04FD430C8}Registry, COM; .NET ToString("B") in lower case
No hyphens6ba7b8109dad11d180b400c04fd430c8.NET "N", Python .hex
URNurn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8XML and other URN-based formats
0x hex, RFC order0x6BA7B8109DAD11D180B400C04FD430C8MySQL BINARY(16) literals
Base64, RFC ordera6e4EJ2tEdGAtADAT9QwyA==Compact text, 24 characters
Hex, Microsoft order10b8a76bad9dd11180b400c04fd430c8Guid.ToByteArray(), SQL Server binary(16), Python bytes_le

The generator's Format menu produces the first seven directly, and the decoder shows the BINARY(16) hex and Base64 of any UUID you paste. It accepts braces, urn:uuid:, 0x, missing hyphens and any case.

Comparing GUIDs and UUIDs safely

  1. Parse, then compare. Guid.Parse, uuid.UUID(), UUID.fromString and a ::uuid cast all turn text into a 128-bit value; braces and case disappear.
  2. If you have to compare strings, normalise first: strip braces and urn:uuid:, lower-case, then compare.
  3. Exchange text or RFC-order bytes across systems. Raw ToByteArray() output is only safe when both ends are .NET or SQL Server.
  4. Store native types: uuid, uniqueidentifier or BINARY(16), not 36-character strings that double the index size.

Two more habits are worth a rule. Guid.Empty is the Nil UUID, all zeros; treat it as "no value", never as a real key, and reject it at the API boundary. And if GUIDs end up in a text column anyway, a unique index under a case-sensitive collation happily accepts 6BA7B810-… and 6ba7b810-… as two different rows. A native type closes that hole for free.

For the one-liners that create these values on each platform, see how to generate a UUID in code.

FAQ

Is a GUID the same as a UUID?

Yes. GUID is the Microsoft name for the 128-bit identifier defined in RFC 9562. The differences are habits: upper case and braces, a legacy variant on very old GUIDs, and mixed-endian byte order in .NET and SQL Server binary form.

Are GUIDs case sensitive?

No. The digits are hexadecimal, so A and a are the same value. RFC 9562 says to output lower case and accept any case; compare parsed values, not raw strings.

Why does my GUID change after converting it to bytes?

.NET Guid.ToByteArray() and SQL Server store the first three groups little-endian. Read those bytes in RFC order and you get a different ID; use text or the big-endian overload instead.

Can I store a .NET GUID in a PostgreSQL uuid column?

Yes. Send it as a string, or as RFC-order bytes with ToByteArray(bigEndian: true) in .NET 8 and later. PostgreSQL accepts upper case and braces on input and prints lower case.

Open the UUID generator

Keep reading