Choose bits first, the alphabet second
Token strength is a count of random bits. The encoding decides how many characters carry those bits, nothing more. A 128 bit token is 32 hex characters, 22 base64url characters, or 26 in Crockford base32, and all three resist guessing equally well.
| Bits | hex | base64url | base62 | Crockford base32 |
|---|---|---|---|---|
| 64 | 16 | 11 | 11 | 13 |
| 80 | 20 | 14 | 14 | 16 |
| 128 | 32 | 22 | 22 | 26 |
| 192 | 48 | 32 | 33 | 39 |
| 256 | 64 | 43 | 43 | 52 |
| 512 | 128 | 86 | 86 | 103 |
The guessing column in the tool assumes an attacker with an offline copy of your token hashes and a rig doing a trillion guesses per second. Half of a 64 bit space falls in about 107 days. Half of a 128 bit space takes roughly 5 × 1018 years. Keep 64 and 80 bits for codes the server checks one attempt at a time, behind a rate limit.
The page opens at 256 bits, enough for any secret a server stores. 512 bits earns its length in one place: an HMAC-SHA512 key. RFC 2104 advises a key at least as long as the hash output, and SHA-512 outputs 64 bytes. Past that, extra bits only make the token longer to paste.
Which preset fits which job
Each chip above loads the row below. Lifetime and storage are where most token bugs live, so they sit next to the length.
| Job | Bits | Encoding | Lifetime | Server keeps |
|---|---|---|---|---|
| Session ID | 128 | base64url | Until logout or idle timeout | Raw value in the session store |
| CSRF token | 128 | hex | One per session | Raw value in the session |
| Password reset | 256 | base64url | 15 to 60 minutes, single use | SHA-256 of the token |
| API key | 192 | base62 with prefix | Until revoked | SHA-256 plus the last 4 characters |
| Webhook secret | 256 | hex | Until rotated | Encrypted raw value |
| Invite code | 80 | Crockford base32, dashed | Days, single use | SHA-256, plus a redemption rate limit |
Base62 for API keys is a usability call. A double-click in a terminal or a browser selects the whole key, while the - in base64url splits the selection in half.
Why API keys carry a prefix
GitHub issues personal tokens starting with ghp_, and Stripe uses sk_live_ for secret keys. Secret scanners match those prefixes in public commits and alert the issuer within minutes of a leak. A prefix also tells whoever reads a log line which system a key belongs to, and whether it is live or test.
A prefix adds zero bits. tx_live_ followed by 192 random bits is a 192 bit key, so size the random part as if the prefix were absent. The prefix field here accepts letters, digits, _, - and ., up to 16 characters.
Store a hash of the token, not the token
If someone dumps your password_resets table, raw tokens in it are working login links. Store SHA-256 instead and compare in constant time:
$token = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
$stored = hash('sha256', $token); // save this, email $token
// on the reset request
if (hash_equals($stored, hash('sha256', $request->input('token')))) {
// valid, now delete the row
}
A fast hash is fine here. bcrypt and Argon2 exist to slow down guesses against short human passwords. A 256 bit random token has no short guesses to slow down. The one job in the table where hashing breaks things is the webhook secret: both sides need the raw value to compute the HMAC, so encrypt it at rest instead.
How this generator picks characters
Hex and base64url tokens come straight from random bytes, so 16 bytes give exactly 128 bits. Base62 and base32 need one character at a time, and the naive byte % 62 is biased. 256 does not divide evenly by 62, so bytes 248 to 255 would land on the first eight characters a second time and make 0 through 7 slightly more common.
The tool throws those eight byte values away and draws again, a method called rejection sampling. Every character ends up equally likely, and the length shown is rounded up so the token never falls short of the bits you asked for. A 128 bit base62 token is 22 characters, about 131 bits.
Nothing leaves the page. Open your browser's network panel and press Generate: no request fires. Reloading clears every token.
Where this page is the wrong tool
- Per-user tokens in production. Session IDs and reset links belong to the server at request time. Copy the snippet from the code panel into your app rather than pasting tokens from a browser into a database.
- JWTs. A JWT is signed JSON claims, not a random string. Use the JWT Encoder to build one and generate its HMAC secret here at 256 bits.
- Passwords people type. Base62 noise is hard to type on a phone. The Password Generator handles passphrases and symbol rules.
- Sortable IDs. These tokens carry no timestamp, so they scatter across a database index. Use ULID or UUID v7 for primary keys.
- Large batches. One click makes up to 100 tokens. For thousands, run the shell line in a loop.