Random hash generator
A random value in the shape of a hash: 32 bytes rendered as 64 hexadecimal characters, the width of a SHA-256 digest.
Read this first: what this page actually does
This page produces a random value with the same shape as a hash. By default that is 32 random bytes rendered as 64 hexadecimal characters, which is exactly the width of a SHA-256 digest. It does not hash anything. There is no input field, and nothing you type anywhere on this site is turned into a digest.
The search term is genuinely ambiguous, so it is worth separating the two things it can mean. Hashing is deterministic: the same input always gives the same digest, and the digest is a function of your data. Generating is the opposite: the output depends on nothing but the random source, and asking for it twice gives two different answers. If you need the digest of a file or a string, you want a hashing tool, or one line of code in whatever language you are already in.
What this page is for is the case where you need a value that looks like a hash and does not have to be one.
A hash is a function of its input. A random value is a function of nothing. If you need to verify something later, you need the first. If you need an opaque value nobody can predict, you need the second.
When a digest-shaped random value is the right answer
Test fixtures are the common case. Code that stores content hashes, commit SHAs, ETags or file digests needs realistic values to exercise the column widths, the indexes, the serialisation and the display truncation. A random 64-character hex value does all of that without you having to hash anything, and it will not accidentally collide with a real digest that means something.
The other case is an opaque identifier. If you want a value that reads as a system-generated fingerprint rather than a sequence number, 32 random bytes in hex is unguessable, fixed width, safe in URLs and filenames, and comparable as a plain string.
There is a subtle reason not to reach for hashing here even when it feels natural. Hashing 32 random bytes with SHA-256 gives you a 32-byte output, but you already had 32 bytes of randomness going in. The hash adds no entropy; it only moves it around. If the goal is an unpredictable value, generate it and stop.
| Algorithm | Output bits | Bytes | Hex characters |
|---|---|---|---|
| MD5 | 128 | 16 | 32 |
| SHA-1 | 160 | 20 | 40 |
| SHA-224 | 224 | 28 | 56 |
| SHA-256 | 256 | 32 | 64 |
| SHA-384 | 384 | 48 | 96 |
| SHA-512 | 512 | 64 | 128 |
A hash of a low-entropy input is not a secret
This is the mistake the ambiguity in the search term tends to lead to, and it is worth being blunt about. Hashing is not encryption. It has no key, it is public, and it is deterministic. If the set of possible inputs is small, the attacker does not reverse the hash - they simply hash every candidate and compare.
The numbers make it concrete. There are ten billion possible ten-digit inputs, which is about 2^33.2. On a single RTX 5090, hashcat v6.2.6-851 does SHA-256 at 28.35 GH/s and MD5 at 220.6 GH/s. So every ten-digit value can be hashed and checked in well under half a second against SHA-256, and in under a tenth of a second against MD5. Phone numbers, national insurance and social security numbers, email addresses, sequential database IDs, dates of birth and postcodes are all in this category. Publishing their hashes publishes them.
Salting does not fix this. A salt defeats precomputed tables and makes each target cost its own search, but the search itself is still cheap when the input space is small. What helps is a deliberately slow function - bcrypt, scrypt or Argon2 - and even that only buys a factor, not a category change. On the same hardware bcrypt at cost 5 runs at 304.8 kH/s against MD5 at 220.6 GH/s, which is roughly six orders of magnitude, and that is the honest size of the win. For anything that must genuinely be unguessable, use a random value from a cryptographic source, which is what this page produces.
If you want a deterministic identifier
Sometimes the reason people reach for a hash is that they want the same input to always produce the same identifier. That is a real requirement and there is a standard answer for it: a name-based UUID. RFC 9562 defines version 5, which is SHA-1 over a namespace identifier and a name, and version 3, which is the older MD5 form. The same namespace and name always yield the same UUID, on any machine, with no coordination.
One specification detail catches people out. There is no version 5 with SHA-256. RFC 9562 section 5.5 states that SHA-256-derived name-based UUIDs must not use version 5 and must live in the version 8 space. If a library offers you a SHA-256 name-based UUID labelled v5, it is not producing what the RFC describes.
Whichever you choose, remember what determinism costs: a name-based identifier is exactly as guessable as the name it was derived from. It is an identifier, never a secret.
Doing this in your own code
JavaScript (browser and Node 19+)
// The argument is BYTES. 16 bytes renders as 32 hex characters.
function randomHex(bytes) {
const b = crypto.getRandomValues(new Uint8Array(bytes));
return [...b].map((v) => v.toString(16).padStart(2, "0")).join("");
}
randomHex(16); // 32 characters, 128 bits
randomHex(32); // 64 characters, 256 bits
// Node has a shorter path that does the same thing:
// require("node:crypto").randomBytes(16).toString("hex") crypto.getRandomValues() throws above 65536 bytes in a single call, so anything larger has to be filled in chunks.
Python 3.6+
import secrets
secrets.token_bytes(16) # 16 raw bytes
secrets.token_hex(16) # 32 characters, 128 bits
secrets.token_hex(32) # 64 characters, 256 bits
# token_hex takes a byte count, not a character count.
# token_hex(32) is 64 characters. This trips people up constantly. secrets draws from os.urandom, which is the operating system CSPRNG. random.random() is not, and must never be used here.
Go
import (
"crypto/rand"
"encoding/hex"
)
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
panic(err) // never carry on after an entropy failure
}
hex.EncodeToString(b) // 32 characters Rust
// getrandom = "0.3"
let mut b = [0u8; 16];
getrandom::fill(&mut b).expect("entropy source unavailable");
let s: String = b.iter().map(|v| format!("{:02x}", v)).collect();
// s.len() == 32 On getrandom 0.2 the same function is named getrandom::getrandom.
PHP 7+
// random_bytes() throws on failure. Never fall back to rand() or mt_rand().
$raw = random_bytes(16);
bin2hex($raw); // 32 characters, 128 bits
strlen($raw); // 16 Java 17+
import java.security.SecureRandom;
import java.util.HexFormat;
byte[] b = new byte[16];
new SecureRandom().nextBytes(b);
HexFormat.of().formatHex(b); // 32 lowercase characters C# / .NET 6+
using System.Security.Cryptography;
byte[] b = RandomNumberGenerator.GetBytes(16);
Convert.ToHexString(b); // 32 characters, UPPERCASE
Convert.ToHexString(b).ToLowerInvariant(); // lowercase Convert.ToHexString returns uppercase. Most Unix tooling emits lowercase, so normalise before comparing strings.
Shell
# The number after -hex is BYTES, not characters.
openssl rand -hex 16 # 32 characters, 128 bits
openssl rand -hex 32 # 64 characters, 256 bits
# Straight from the kernel, no openssl:
head -c 16 /dev/urandom | xxd -p
# Raw bytes to a file:
head -c 32 /dev/urandom > key.bin openssl rand -hex 32 prints 64 characters. If you wanted a 32-character string you asked for twice as much as you meant to.
Questions
Can I hash my own text with this tool?
No. This page generates a random value in the shape of a hash; it has no input and computes no digest. This site generates values and does not transform values you supply.
So what exactly does the output represent?
Thirty-two random bytes from crypto.getRandomValues(), rendered as 64 lowercase hexadecimal characters. That is the same width as a SHA-256 digest, which is why it looks like one, but it is not derived from any input.
Is a random 64-character hex value as unpredictable as a SHA-256 hash?
More so, in the only sense that matters. A SHA-256 digest is exactly as unpredictable as its input; if the input is guessable, so is the digest. A random 64-character hex value carries a full 256 bits regardless.
Why do people say you cannot reverse a hash?
Because the function itself is not invertible. But an attacker does not need to invert it. They guess inputs, hash each one and compare, which is fast when the input space is small. Irreversibility protects nothing when there are only ten billion candidates.
Which digest width should I pick here?
Match whatever you are standing in for. Thirty-two hex characters looks like MD5, 40 looks like SHA-1 or a git commit, 64 looks like SHA-256 and 128 looks like SHA-512. For a value used as a secret rather than as a fixture, 64 characters gives 256 bits and is a sensible default.
Is MD5 fine if I only need a random-looking value?
The question does not arise here, because nothing is being hashed. If you are choosing a hash function elsewhere, MD5 is broken for collision resistance and should not be used for anything security-related, though it remains acceptable as a non-cryptographic checksum.