UUID spiegato: struttura a 128 bit, versioni e casi d’uso reali
Ogni volta che ti registri a un servizio, viene creato un identificatore univoco per il tuo account. Ogni richiesta API porta un trace ID. Ogni riga in un database distribuito ha bisogno di una chiave primaria che non collida con chiavi generate su altre macchine. La soluzione dietro tutto questo? UUID — Identificatore Univoco Universale.
Questa guida spiega cosa sono gli UUID, come sono strutturati, cosa fa ogni versione sotto il cofano, e quando usarli (o evitarli).
UUID a colpo d’occhio
Un UUID è un identificatore a 128 bit (16 byte) progettato per essere globalmente univoco senza richiedere un’autorità centrale. È scritto come 32 cifre esadecimali nel formato canonico 8-4-4-4-12:
550e8400-e29b-41d4-a716-446655440000
|------| |--| |--| |--| |----------|
8 hex 4 4 4 12 hex
Sono 32 caratteri esadecimali + 4 trattini = 36 caratteri in totale. I trattini sono puramente cosmetici: non portano dati.
Fatti chiave:
- 128 bit = 2¹²⁸ ≈ 3.4 × 10³⁸ valori possibili
- Standardizzato dall’RFC 9562 (maggio 2024, sostituisce RFC 4122)
- Chiamato anche GUID (Globally Unique Identifier) negli ecosistemi Microsoft: stesso formato, nome diverso
- Supportato nativamente da PostgreSQL (tipo
uuid), MySQL (BINARY(16)oCHAR(36)) e praticamente ogni linguaggio di programmazione
Anatomia di un UUID
Ogni UUID codifica due campi di metadati in posizioni di bit fisse, indipendentemente dalla versione:
550e8400-e29b-41d4-a716-446655440000
^ ^
| |
Versione┘ └-Variante
Campo versione (bit 48–51)
La 13ª cifra esadecimale (prima cifra del terzo gruppo) identifica la versione UUID:
| Cifra esad. | Versione | Metodo |
|---|---|---|
1 | v1 | Timestamp + indirizzo MAC |
3 | v3 | Hash MD5 di namespace + nome |
4 | v4 | Crittograficamente casuale |
5 | v5 | Hash SHA-1 di namespace + nome |
6 | v6 | Timestamp riordinato (RFC 9562) |
7 | v7 | Timestamp Unix + casuale (RFC 9562) |
8 | v8 | Personalizzato / specifico dell’implementazione |
Campo variante (bit 64–65)
La 17ª cifra esadecimale (prima cifra del quarto gruppo) identifica la variante. Per gli UUID RFC 4122/9562, i primi bit sono 10, il che significa che questa cifra esadecimale è sempre 8, 9, a o b.
Esempio di scomposizione
550e8400-e29b-41d4-a716-446655440000
↑ ↑
4 → v4 a → variante RFC 4122
Questo è un UUID v4 (casuale), variante RFC 4122/9562.
Versioni UUID spiegate
Versione 1: Timestamp + indirizzo MAC
UUID v1 era il design originale. Codifica:
- Timestamp a 60 bit — intervalli di 100 nanosecondi dal 15 ottobre 1582 (la riforma del calendario gregoriano)
- Sequenza di clock a 14 bit — contatore di monotonia per prevenire duplicati su rollback dell’orologio
- Nodo a 48 bit — tipicamente l’indirizzo MAC della macchina
| Timestamp | Ver | Clk |Var| Node (MAC) |
| 60 bit | 4b | 14b |2b | 48 bit |
Problemi:
- Espone il tempo di generazione e l’identità hardware (rischio per la privacy)
- Gli indirizzi MAC possono essere falsificati, minando l’unicità
- L’epoch del 1582 è confusa e richiede conversione
Verdetto: Deprecato dall’RFC 9562. Usa v7 invece per UUID basati sul tempo.
Versione 3: Basato sul nome con MD5 (deterministico)
UUID v3 calcola l’hash di un UUID namespace e di una stringa nome usando MD5. Gli stessi input producono sempre lo stesso UUID.
import uuid
# namespace = DNS, name = "example.com"
print(uuid.uuid3(uuid.NAMESPACE_DNS, "example.com"))
# → "9073926b-929f-31c2-abc9-fad77ae3e8eb" (sempre questo valore)
Sono definiti quattro namespace standard:
- DNS:
6ba7b810-9dad-11d1-80b4-00c04fd430c8 - URL:
6ba7b811-9dad-11d1-80b4-00c04fd430c8 - OID:
6ba7b812-9dad-11d1-80b4-00c04fd430c8 - X.500:
6ba7b814-9dad-11d1-80b4-00c04fd430c8
Verdetto: Funzionale, ma preferisci v5: SHA-1 è più forte di MD5.
Versione 4: Casuale — La più popolare
UUID v4 riempie 122 bit con dati casuali crittograficamente sicuri (i restanti 6 bit sono riservati per i campi versione e variante).
| Casuale | Ver | Casuale |Var| Casuale |
| 48 bit | 4b | 12 bit |2b | 62 bit |
Con 2¹²² ≈ 5.3 × 10³⁶ valori possibili, la probabilità di collisione è astronomicamente bassa. Per raggiungere il 50% di probabilità di almeno una collisione, ti servirebbero circa 2.71 × 10¹⁸ UUID: 2.71 quintilioni.
// Ogni browser moderno e Node.js supportano questo
const id = crypto.randomUUID();
console.log(id); // → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
Punti di forza: semplice, privato, supportato universalmente, nessun coordinamento necessario.
Debolezza: la distribuzione casuale causa frammentazione dell’indice B-tree quando usato come chiave primaria del database. Per casi d’uso pesanti per il database, considera v7.
Versione 5: Basato sul nome con SHA-1 (deterministico)
Identico a v3 ma usa SHA-1 invece di MD5. Gli stessi input producono sempre lo stesso UUID.
import uuid
print(uuid.uuid5(uuid.NAMESPACE_DNS, "example.com"))
# → "cfbff0d1-9375-5685-968c-48ce8b15ae17" (sempre questo valore)
Casi d’uso:
- Generare ID stabili da URL o nomi DNS
- Chiavi di storage indirizzabile per contenuto
- Fixture di test riproducibili
Importante: v3 e v5 NON sono pensati per la sicurezza. Sono deterministici: chiunque conosca il namespace e il nome può riprodurre l’UUID.
Versione 7: Timestamp Unix + casuale (raccomandato per nuovi progetti)
UUID v7 è la versione più recente, introdotta nell’RFC 9562 (maggio 2024). Codifica:
- Timestamp Unix a 48 bit in millisecondi — monotonicamente crescente
- 74 bit di casualità crittografica
| Timestamp Unix (ms) | Ver | rand_a |Var| rand_b |
| 48 bit | 4b | 12 bit |2b | 62 bit |
Questo significa che gli UUID v7 sono naturalmente ordinati per tempo di creazione: gli UUID più recenti sono sempre lessicograficamente maggiori di quelli più vecchi. Questa proprietà li rende ideali per le chiavi primarie del database, dove gli indici B-tree restano sequenziali invece di frammentarsi casualmente.
import { v7 as uuidv7 } from "uuid";
const id1 = uuidv7(); // generato a T₁
const id2 = uuidv7(); // generato a T₂ (T₂ > T₁)
console.log(id1 < id2); // → true (confronto lessicografico)
Perché conta per i database: la proprietà sequenziale di v7 riduce gli split di pagina dell’indice fino al 90% rispetto a v4, risultando in inserimenti più veloci, indici più piccoli e migliori prestazioni della cache.
UUID vs GUID — Qual è la differenza?
Non c’è differenza funzionale. GUID (Globally Unique Identifier) è il nome Microsoft per UUID, usato in Windows, .NET, COM e SQL Server. Il formato è identico: 128 bit, esadecimale 8-4-4-4-12.
L’unica differenza cosmetica: gli strumenti Microsoft a volte mostrano i GUID in maiuscolo con parentesi graffe:
UUID: 550e8400-e29b-41d4-a716-446655440000
GUID: {550E8400-E29B-41D4-A716-446655440000}
Se qualcuno chiede della “differenza tra UUID e GUID,” la risposta è: branding.
Valori UUID speciali
L’RFC 9562 definisce due UUID speciali:
| Nome | Valore | Scopo |
|---|---|---|
| Nil UUID | 00000000-0000-0000-0000-000000000000 | Rappresenta assenza di valore (come null) |
| Max UUID | ffffffff-ffff-ffff-ffff-ffffffffffff | Marcatore di confine o valore sentinella |
Non usare mai questi come identificatori effettivi: per definizione non sono univoci.
Probabilità di collisione: il problema del compleanno
Il “problema del compleanno” calcola quanti UUID ti servono prima che una collisione diventi probabile. Per UUID v4 (122 bit casuali):
| UUID generati | Probabilità di collisione |
|---|---|
| 1 milione | ~10⁻²² (virtualmente impossibile) |
| 1 miliardo | ~10⁻¹⁶ (ancora trascurabile) |
| 2.71 × 10¹⁸ | 50% (il “limite del compleanno”) |
Per metterlo in contesto: se generassi 1 miliardo di UUID al secondo, ci vorrebbero 86 anni per raggiungere il 50% di probabilità di una singola collisione. In pratica, guasto hardware, bug software e raggi cosmici sono tutti più probabili di causare un duplicato che la matematica dell’UUID v4.
La formula: p(n) ≈ n² / (2 × 2¹²²)
Come validare un UUID
Un UUID valido corrisponde a questo pattern regex (case-insensitive):
^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
Questo verifica:
- Il formato esadecimale 8-4-4-4-12
- La cifra di versione è 1–7 (posizione 15)
- Il nibble di variante inizia con 8, 9, a o b (posizione 20)
function isValidUUID(str) {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str);
}
isValidUUID("550e8400-e29b-41d4-a716-446655440000"); // → true
isValidUUID("not-a-uuid"); // → false
Generare UUID in ogni linguaggio
JavaScript / TypeScript
// Browser e Node.js — v4 integrato
crypto.randomUUID();
// pacchetto npm uuid — supporta v1, v3, v4, v5, v7
import { v4, v7 } from "uuid";
v4(); // casuale
v7(); // ordinato nel tempo
Python
import uuid
uuid.uuid4() # casuale
uuid.uuid5(uuid.NAMESPACE_DNS, "example.com") # deterministico
# uuid.uuid7() pianificato per Python 3.14+
Go
import "github.com/google/uuid"
uuid.New() // v4 casuale
uuid.Must(uuid.NewV7()) // v7 ordinato nel tempo
Java
import java.util.UUID;
UUID.randomUUID(); // v4 casuale
// UUID v7: usa com.fasterxml.uuid o java.util.UUID in JDK 21+
SQL (PostgreSQL)
-- v4 (PostgreSQL 13+)
SELECT gen_random_uuid();
-- v7 (PostgreSQL 18+)
SELECT uuidv7();
Casi d’uso comuni
Chiavi primarie del database
Gli UUID ti permettono di generare ID ovunque — nell’applicazione, sul client, all’edge — senza un round trip al database. Questo abilita architetture offline-first e semplifica i sistemi distribuiti. Usa v7 per le migliori prestazioni dell’indice, o v4 se non ti importa l’ordinamento.
Tracciamento delle richieste API
Assegna un UUID a ogni richiesta API al punto di ingresso (gateway, load balancer). Passalo attraverso tutti i servizi a valle in un header come X-Request-ID. Questo rende banale correlare i log tra microservizi.
Chiavi di idempotenza
Le API usano gli UUID come chiavi di idempotenza per garantire che le richieste riprovate non creino risorse duplicate. Il client genera un UUID prima del primo tentativo e invia lo stesso UUID nei retry.
Identificatori di sessione
Gli UUID forniscono unicità sufficiente per prevenire collisioni di sessione tra grandi basi utente. A differenza degli interi auto-incrementali, non possono essere enumerati: un attaccante non può indovinare ID di sessione validi incrementando un numero.
Storage indirizzabile per contenuto
UUID v5 genera ID deterministici dal contenuto. Dato lo stesso input, ottieni sempre lo stesso UUID, utile per deduplica, caching e build riproducibili.
Considerazioni di sicurezza
Gli UUID NON sono token di sicurezza
Gli UUID sono progettati per unicità, non segretezza. Problemi chiave:
- UUID v1 rivela il timestamp di generazione e l’indirizzo MAC
- UUID v4 ha 122 bit casuali ma una struttura prevedibile (i bit di versione/variante sono fissi)
- UUID v3/v5 sono deterministici: chiunque conosca il namespace e il nome può riprodurre l’UUID
Per token di sicurezza, chiavi API o segreti di sessione, usa un CSPRNG dedicato con 128+ bit di pura casualità:
// Per token di sicurezza — NON un UUID, ma completamente casuale
const token = Array.from(crypto.getRandomValues(new Uint8Array(32)))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
UUID v7 espone il tempo di creazione
I primi 48 bit di un UUID v7 codificano il timestamp di creazione in millisecondi. Chiunque riceva un UUID v7 può estrarre quando è stato creato:
const hex = "01906b5e-4a3e-7234-8f56-b8c12d4e5678".replace(/-/g, "").slice(0, 12);
new Date(parseInt(hex, 16));
// → 2024-07-01T12:34:56.000Z
Se il tempo di creazione è informazione sensibile, usa v4 invece.
Non usare gli UUID per prevenire l’enumerazione
Sebbene gli UUID siano più difficili da indovinare degli interi sequenziali, non dovrebbero essere il tuo unico meccanismo di controllo accessi. Imponi sempre verifiche di autorizzazione: non fare affidamento sull’oscurità dell’URL.
Domande frequenti
Perché ci sono trattini negli UUID?
I trattini nel formato 8-4-4-4-12 sono puramente per leggibilità umana. Non portano dati e vengono ignorati durante il parsing. Alcuni sistemi memorizzano gli UUID senza trattini (32 caratteri esadecimali), il che è ugualmente valido.
Due UUID possono mai essere uguali?
Teoricamente sì, praticamente no. Per UUID v4 con 122 bit casuali, la probabilità di generare due UUID identici è circa 1 su 5.3 × 10³⁶ per qualsiasi data coppia. A tassi di generazione del mondo reale, è più probabile essere colpiti da un fulmine mentre si vince alla lotteria che incontrare una collisione UUID.
Gli UUID sono sequenziali?
Solo alcune versioni. UUID v1, v6 e v7 contengono timestamp e si ordinano cronologicamente. UUID v4 è completamente casuale senza ordinamento. UUID v3 e v5 sono deterministici ma non ordinati.
Quanto storage usa un UUID?
- Binario: 16 byte (128 bit) — lo storage più efficiente
- Stringa (con trattini): 36 byte (ASCII)
- Stringa (senza trattini): 32 byte (ASCII)
La maggior parte dei database memorizza gli UUID in formato binario internamente. Il tipo nativo uuid di PostgreSQL usa esattamente 16 byte.
Dovrei usare UUID o auto-increment per le chiavi primarie?
Auto-increment è più semplice per applicazioni single-database (più piccolo, più veloce, sequenziale). UUID è migliore per sistemi distribuiti (genera ovunque, nessun coordinamento, sicuro per il merge). Se usi UUID, preferisci v7 per le migliori prestazioni del database.
Cos’è l’RFC 9562?
L’RFC 9562, pubblicato a maggio 2024, è lo standard UUID più recente. Sostituisce l’RFC 4122 e introduce formalmente le versioni UUID 6, 7 e 8. Deprecata v1 a favore di v6/v7 e definisce i valori nil e max UUID. Se stai implementando generazione o validazione UUID, l’RFC 9562 è il riferimento autorevole.
Posso usare gli UUID in diversi linguaggi di programmazione?
Sì. Il formato UUID (128 bit, esadecimale 8-4-4-4-12) è agnostico al linguaggio. Un UUID generato in JavaScript verrà correttamente analizzato in Python, Go, Java o qualsiasi altro linguaggio con supporto UUID. Questa interoperabilità è uno dei più grandi punti di forza dell’UUID.
Genera, decodifica e valida UUID istantaneamente con il nostro Generatore UUID: supporta v1, v4, v5 e v7 con generazione batch, 100% nel tuo browser.
Stai scegliendo tra versioni UUID per il tuo prossimo progetto? Leggi il nostro confronto UUID v4 vs v7 vs ULID vs Snowflake per una guida pratica alla selezione con benchmark di database ed esempi di codice.