Short UUID generator
The same 128-bit UUID written in base58 as 22 characters, fully reversible, with no entropy added or lost.
It is the same value, written differently
A canonical UUID is 128 bits rendered in base 16, which needs 32 digits, plus four hyphens for readability. Rendering the identical 128-bit integer in base 58 needs 22 characters. Nothing has been truncated, hashed or compressed. The number is the same number, and running the conversion backwards gives you the original UUID exactly.
The arithmetic is straightforward: 128 divided by the base-2 logarithm of 58 is 21.85, so 22 characters are needed and 22 are sufficient. Those 22 characters can express 6.24e38 distinct values against the 3.40e38 that 128 bits requires, so there is a little slack at the top of the range. That slack is why encoders pad to a fixed 22 characters with the first character of the alphabet: without padding, a UUID that happens to start with several zero bytes encodes to a shorter string, and variable-length identifiers are a nuisance in a fixed-width column.
The entropy is whatever the source UUID had. A short form of a version 4 UUID carries 122 random bits, the same 122 it had before. The 22-character base58 alphabet has a theoretical capacity of 128.88 bits, and quoting that number as the strength of the identifier would be wrong by about seven bits. Re-encoding never adds entropy.
| Encoding | Base | Characters | Separators | Bit-aligned |
|---|---|---|---|---|
| Canonical UUID | 16 | 32 | 4 hyphens | Yes, 4 bits per character |
| Compact hex | 16 | 32 | None | Yes, 4 bits per character |
| Base58 | 58 | 22 | None | No, whole-integer conversion |
Why base58 and not base64
Base58 comes from Bitcoin addresses, and its alphabet is the 62 alphanumeric characters minus four: the digit zero, capital O, capital I and lowercase l. Those are the pairs that people confuse when reading a value off a screen, copying it from a printed page, or reciting it over a phone. Dropping them costs about 0.1 bits per character and removes an entire class of support ticket.
Base64 is denser, at 22 characters for 132 bits, but its alphabet includes plus and slash, both of which need percent-encoding in a URL path. The URL-safe variant swaps those for hyphen and underscore, which solves that problem but reintroduces the look-alike characters and adds a character that some tokenisers treat as a word boundary. Base58 has no such characters, needs no encoding anywhere in a URL, and double-clicking one selects the whole thing.
Base 58 is not a power of two, which is the significant technical difference. Base64 and base32 slice the input into fixed bit groups and map each group to a character independently. Base58 cannot: it is a conversion of the entire 128-bit integer, so encoding is a repeated division and decoding is a repeated multiplication, and you cannot process the value in a stream. For a fixed 16-byte input that is irrelevant to performance and worth knowing when you read the code.
Two orderings of the base58 alphabet are in circulation. The Bitcoin ordering puts digits, then uppercase, then lowercase (123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz); the Flickr ordering puts digits, then lowercase, then uppercase (123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ). The character set is identical but the values are not, so the same UUID encodes to two different strings. The generator on this page uses the Bitcoin ordering, because it is ascending by code point and therefore sorts. The npm short-uuid package defaults to the Flickr ordering, so a value generated here will not decode through short-uuid unless you pass the Bitcoin alphabet to createTranslator. Whichever you pick, record it, because the decoder has to match.
When 22 characters instead of 36 is worth anything
Most of the time it is not, and the honest answer is that a canonical UUID in a URL is fine. The cases where the shorter form earns its keep are specific.
The first is anything printed or scanned. A QR code encoding 22 alphanumeric characters is materially less dense than one encoding 36, which means a smaller printed square at the same error-correction level, or the same square with more correction. On a shipping label or a conference badge that is a real difference.
The second is anything a human reads aloud, types from a screen, or checks against a paper record. Fourteen fewer characters and four fewer look-alike pairs both help, and the no-hyphen form selects cleanly on a double-click where a hyphenated UUID often does not.
The third is URL budget, though carefully. Fourteen characters is not going to save a URL that is already too long, and it does nothing for the search engine that reads it. It matters when several identifiers appear in one path, or when the identifier is going into a QR code or a short link that has its own length constraint.
What it is not is a way to hide anything. A short UUID of a version 7 value still decodes to a timestamp, and a short UUID of a version 5 value is still a deterministic hash of a public name. The encoding changes how the value looks and nothing about what it contains.
What it is not
Short UUID is a library convention, not a standard. There is no RFC and no registered format. Nothing will accept a 22-character base58 string in a uuid column, and nothing will validate it as a UUID. The correct pattern is to store the canonical 16-byte UUID and encode to base58 only at the edge where the value is displayed or put into a URL, decoding on the way back in.
It also does not sort the way you might assume. If the alphabet is in ascending code-point order, as the Bitcoin ordering is, then a fixed-width padded base58 string sorts lexicographically in the same order as the underlying integer, so a shortened version 7 UUID still sorts by creation time as text. The Flickr ordering puts lowercase before uppercase, which is not ASCII order, so lexicographic sorting of those strings does not match the numeric order at all. If you intend to sort the encoded form, use an alphabet whose order is ascending and pad to a fixed width.
If what you want is a short identifier rather than a short UUID, generating one directly is usually better than shortening one. Nano ID at 21 characters carries 126 random bits, more than a version 4 UUID, in one fewer character and with no conversion step. ULID at 26 characters is time-sortable by design. Reach for short UUID when the canonical UUID already exists and has to keep existing.
Doing this in your own code
JavaScript (short-uuid)
// short-uuid 6.x. Version 6.0.0 removed the default export and the
// .new() method, so the pre-6 "short()" form no longer runs.
import { createTranslator, generate, constants } from "short-uuid";
generate(); // 'mhvXdrZT4jP5T8vBxuvm75'
const translator = createTranslator(); // default alphabet
translator.generate(); // .new() was removed in 6.0.0
translator.toUUID("mhvXdrZT4jP5T8vBxuvm75"); // back to canonical
translator.fromUUID("a44521d0-0fb8-4ade-8002-3385545c3318");
// Name the alphabet explicitly if anything else has to decode the value.
// This is the exact default, lowercase before uppercase:
// 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ
const flickr = createTranslator(constants.flickrBase58); // same as the default The library defaults to the Flickr ordering of the base58 alphabet, not the Bitcoin ordering. Encoder and decoder must agree or you get a different UUID back. The two no-dependency examples below use the Bitcoin ordering, so their output does not interoperate with this one.
JavaScript (no dependency)
const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
function uuidToBase58(uuid) {
let n = BigInt("0x" + uuid.replace(/-/g, ""));
let s = "";
while (n > 0n) { s = BASE58[Number(n % 58n)] + s; n /= 58n; }
return s.padStart(22, BASE58[0]); // fixed width, so it sorts
}
function base58ToUuid(s) {
let n = 0n;
for (const c of s) n = n * 58n + BigInt(BASE58.indexOf(c));
const h = n.toString(16).padStart(32, "0");
return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20)}`;
} Base 58 is not a power of two, so this is a whole-integer base conversion. You cannot do it in fixed-size chunks the way base64 and base32 work. This constant is the Bitcoin ordering, which is ascending by code point and therefore sorts; swap it for the Flickr ordering if you need to decode values produced by the short-uuid package.
Python
import uuid
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def encode(u: uuid.UUID) -> str:
n, out = u.int, ""
while n:
n, r = divmod(n, 58)
out = ALPHABET[r] + out
return out.rjust(22, ALPHABET[0])
def decode(s: str) -> uuid.UUID:
n = 0
for c in s:
n = n * 58 + ALPHABET.index(c)
return uuid.UUID(int=n) Questions
Does shortening a UUID lose any randomness?
No. It is a base conversion of the same 128-bit integer, so the value is preserved exactly and converts back to the original UUID. A shortened version 4 UUID has the same 122 random bits it had before.
Why 22 characters?
Because 128 divided by the base-2 logarithm of 58 is 21.85, so 22 characters are the minimum that can hold every 128-bit value. Encoders pad shorter results to 22 with the first character of the alphabet so that every identifier is the same width.
Which characters are excluded and why?
The digit 0, capital O, capital I and lowercase l. They are the four characters people misread most often in a sans-serif font, and removing them is worth far more in practice than the fraction of a bit per character it costs.
Can I convert a short UUID back to a normal one?
Yes. The short form is a base conversion, not a hash, so decoding is a base-58 to base-16 conversion back. The code samples on this page do it in both directions. The only requirement is that the same alphabet ordering is used to decode as was used to encode: this page generates with the Bitcoin ordering, and the short-uuid package defaults to the Flickr one.
Is short UUID a standard?
No. RFC 9562 defines the 128-bit value and its canonical text form; base58 shortening is a convention popularised by the short-uuid npm package. Store the canonical UUID and treat the short form as a rendering.
Should I use short UUID or Nano ID?
If the UUID already exists and something downstream requires it, shorten it. If you are choosing a new identifier format with no UUID constraint, Nano ID at 21 characters gives 126 random bits with no conversion layer and is the simpler choice.