Generators

Secure Random Data Generator

Generate Base64URL, hex, alphanumeric, or numeric random values.

Cryptographically secure random data
crypto.getRandomValues()
Generated values

Secure Random Data

Generate Base64URL, hex, alphanumeric, or numeric values with crypto.getRandomValues(). Configure length, count, and an optional fixed prefix.

Usage

  • Base64URL works well in URLs, cookies, and API tokens
  • Hex is useful for keys, salts, and debug output
  • A fixed prefix is not random and should not be counted as entropy

Generates tokens and keys from the browser's CSPRNG: Base64URL, lowercase hex, letters plus digits, or digits only, up to 2048 characters each and 100 values per run, with an optional fixed prefix. Use it for API tokens, session IDs, salts, and fixtures that must not be guessable.

Rejection sampling instead of plain modulus

Every character is indexed from one uniformly random byte, but the four alphabets are different sizes: 16 for hex, 10 for digits, 62 for letters plus digits, 64 for Base64URL. A raw byte modulo alphabet length is only unbiased when that length divides 256, which hex satisfies exactly (256 % 16 = 0). Digits and alphanumerics do not: 256 % 10 = 6 and 256 % 62 = 8. The generator therefore computes 256 minus that remainder as an upper bound — 250 for digits, 248 for letters plus digits — and discards any byte at or above it, drawing a fresh batch and retrying. Digits throw away 6 bytes in 256 (2.34%), alphanumerics 8 (3.13%). Without that step the bias is visible: with plain modulus, the eight leading letters A through H would land five times out of 256 instead of four, handing an attacker a 3% head start on the first character.

Base64URL mode draws bytes, then trims

The other three modes pick characters straight from an alphabet. Base64URL mode instead allocates ceil(length x 0.8) + 2 random bytes, flattens them into a binary string, runs btoa, rewrites plus as - and slash as _, strips trailing = padding, then slices to the requested length. The 0.8 factor sits above the 0.75 implied by four characters per three bytes, and the two extra bytes are slack, so the intermediate string is always longer than the target and the slice never falls short; length 1 draws 3 bytes, length 2048 draws 1641. Trimming is not a weakness: each surviving character is one 6-bit group of uniform bytes, all 64 symbols stay equally likely, and every character carries exactly 6 bits, so length 32 is 192 bits and the 43 characters that hold a 32-byte key are 258. This mode never emits padding or raw + and /.

Same length, very different strength

The length field counts characters, so one setting yields four strengths. At length 32, Base64URL gives 192 bits, letters plus digits 190.53, hex 128, digits only 106.30. Reaching 256 bits takes 43 Base64URL characters, 43 alphanumeric, 64 hex, or 78 digits. If a downstream column caps tokens at 40 characters, Base64URL still clears 240 bits while digits fall to roughly 133, so choosing digits for readability is a decision to make with the arithmetic in hand rather than by default. The fixed prefix is concatenated after generation and contributes no entropy at all: sk_live_ followed by 32 Base64URL characters is a 40-character field worth 192 bits, not 256. Length and count accept only integers in 1-2048 and 1-100, checked when the value is regenerated.

Worked example

Characters needed for 256 bits, per character set

Input
target 256 bits, count 1, no prefix
Output
Base64URL 43 - letters plus digits 43 - hex 64 - digits 78

Divide 256 by the bits each symbol carries — log2(64) = 6, log2(62) = 5.954, log2(16) = 4, log2(10) = 3.322 — then round up. Every one of those settings is inside the 1-2048 length field, and the 78-digit case overshoots to 259 bits because the ceiling rounds up.

Limits and pitfalls

  • Length stops at 2048 characters per value and count at 100 values per run; the prefix lives in a separate field (maxlength 128) and sits on top of that, so real length is prefix plus random part.
  • Hex output is lowercase only (0123456789abcdef), so a consumer that compares or stores uppercase hex needs the case converted first or the match will fail.
  • The prefix is pasted verbatim with no escaping: spaces, quotes, and slashes reach the output untouched and can break a cookie, a URL, or a SQL literal downstream.
  • Editing the character set, length, or count field regenerates nothing; the panel only refreshes on Regenerate, so a screenshot can pair new settings with an old list.
  • An empty or fractional length fails validation instead of being rounded, and the result panel is wiped rather than kept, so a half-typed field leaves you with an empty box.

Related tools

Where a raw random string is the wrong instrument

What you are doingThis generatorWhat to reach for instead
A parser downstream expects a UUID, not 32 random charactersBase64URL at length 32 is 192 bits of solid randomness with no structure at all: no hyphens, no version nibble, no RFC 4122 variant bits. UUID.parse() and most uuid columns reject it, and sorting it by time is meaningless because there is no timestamp in it.The UUID tool — a v4 value spends 6 of its 128 bits on the version number and the variant marker and keeps 122 random bits. That 122 is the figure to weigh against the 192 here, not the 128 the name suggests.
The same value has to come out again next week, from a seedNothing is repeatable: the draw comes straight out of crypto.getRandomValues(), there is no seed field and no input, so a fixture file regenerated after a refactor holds entirely different tokens and every snapshot test breaks.Derive from an index instead: HMAC-SHA256(fixture_key, "token-17") and keep the first 32 hex characters — 128 bits, byte-identical on every machine and every run. The HMAC tool on this site computes exactly that in the browser.
A human reads the code off a card or a phone screen and retypes itThe 62-symbol pool keeps 0, O, 1, l and I, five shapes that get confused, which is 8.06% of the alphabet. Across an 8-character code the chance of at least one of them is 1 − (57/62)^8 ≈ 49%, so roughly half the codes you issue contain a character somebody can read wrong.A dozen lines over Crockford Base32 — 32 symbols with I, L, O and U removed — drawing with crypto.randomInt(0, 32). The entropy per character drops from 5.95 to 5 bits and the "is that a zero or an O" ticket disappears.
The string becomes key bytes for AES and the library checks the decoded lengthHex at length 64 decodes to exactly 32 bytes, 256 bits. Base64URL at length 43 also decodes to 32 bytes, but one character more — 44 — yields 33, and the cipher init throws a key-size error instead of truncating.openssl rand -hex 32 when the key must never be rendered in a browser tab at all: it belongs in a KMS or a CI secret store, and copying 64 characters through a clipboard is one extra place for it to leak.
Thousands of rows for a fixture or a seed fileCount stops at 100 per run, and Regenerate replaces the whole list in one output box, so 5,000 values means 50 copy cycles of about 100 lines × 44 characters ≈ 4.4 KB each, with no way to append.for i in $(seq 5000); do openssl rand -hex 16; done > tokens.txt writes 5,000 lines of 32 hex characters, 128 bits each, straight into a file the test harness can read.
The value is a password a person is expected to rememberEvery setting produces noise: 20 alphanumeric characters are 119 bits with no word boundary or rhythm to hold on to, so the only realistic home for it is a password manager.Diceware against the EFF 7,776-word list — 6 words land near 77.5 bits and the sentence-like shape survives the walk to another machine; keep this generator for values that a machine, not a person, will recall.

One concrete scenario

5,000 invite codes printed on cards and typed back by hand

A closed beta prints 5,000 invite codes on cards; users type them at signup, and a duplicate is a refund-level bug. Digits only looks right for a phone keypad, but 8 digits is a 10^8 space, and by the birthday bound 5,000 codes land on a collision with probability near 5,000² / (2 × 10^8) = 12.5% — about one chance in eight that some pair clashes. Ten digits cuts that to 0.125% and still only costs 10 × log2(10) ≈ 33.2 bits, far inside the 2048-character ceiling. Count caps at 100, so the run finishes in 50 batches that overwrite one another: copy the box each time, or move the job to a script.

Frequently Asked Questions

Are the generated values cryptographically secure?
Yes. Everything comes from the Web Crypto API’s crypto.getRandomValues(), backed by the operating system’s CSPRNG — safe for keys, tokens, and salts. Never use Math.random() for these: it is predictable pseudo-randomness.
How long should an API token be?
Rule of thumb: at least 128 bits of entropy (about 22 Base64URL characters or 32 hex); security-critical credentials 256 bits (64 hex). Length sets the enumeration cost, which pairs with server-side rate limits and expiry for a complete design.
Does a fixed prefix hurt randomness?
The prefix contributes no entropy, but it helps humans identify purpose (sk_live_, pat_). Count only the random part when quoting strength — including the prefix overstates security. Also watch total length against field limits downstream.

Content reviewed: