Hash Generator
Hash text with SHA-256, SHA-384, SHA-512, SHA-1 or CRC-32. Reads the input as UTF-8 text or as raw hex bytes, because those give different answers and that is where mismatches come from.
input
The default, and the right answer unless you have a reason.
A string has no bytes until something picks an encoding. UTF-8 is the only defensible default, and a different choice gives a different digest for the same visible characters.
Press Hash when the input is what you want. Nothing is computed while you type, so the digest always belongs to a specific version of the text.
About MD5
The browser’s cryptography has no MD5, and this page does not add one. Hand-writing it is a bad idea and taking a dependency for it costs every visitor a download of an algorithm nobody should be using. If you genuinely need an MD5 digest, something already stores one, and that is the thing worth changing.
Runs in this browser tab, using this browser’s own cryptography via crypto.subtle. Nothing is uploaded, stored or logged.
How to use this tool
A hash turns some bytes into a short fixed-length fingerprint. Change one bit anywhere in the input and the whole fingerprint changes, which is what makes it useful for checking that data arrived intact and for addressing data by its content rather than by a name you have to keep in step. What it is not is encryption: there is no key, and nobody, including the browser, can get your input back out of the digest.
- 1Pick an algorithm. SHA-256 is the one you want unless you have a reason.
- 2Type the input, or paste it. Choose **text** for characters or **hex bytes** for bytes you were handed.
- 3Press **Hash**. Nothing is computed while you type, so the digest always belongs to a specific version of the text.
A string has no bytes until something picks an encoding
This is the single most common reason a hash does not match, and it is invisible because the character looks the same either way. When you type a character into a text field, what gets hashed is not the character — it is whatever bytes the encoder chose. Encode as UTF-8 and `é` is two bytes. Encode as Latin-1 and it is one. The two inputs look identical on screen and produce completely different digests.
So this page always uses UTF-8 for text, because UTF-8 is the only defensible default, and it tells you how many bytes it read. If your input is nine characters and the page reports nine bytes, the text was all ASCII. If it reports eighteen, half of it was not.
"é" as UTF-8 -> 0xC3 0xA9 two bytes
"é" as Latin-1 -> 0xE9 one byte
sha256 of those two byte sequences is not the same value, and
neither of them is wrong. They are hashes of different inputs.Text and hex bytes are different inputs, not two views of one
The other half of the mismatch problem. `0a` can mean the two characters zero and a, or it can mean the single byte whose value is ten. Hashing them gives different answers, and both are correct. This page makes you say which one you mean rather than guessing, because a guess that is right four times in five is a bug that shows up in production.
// The same four characters on screen, in a system config file
sha256("0a") // 6856c5a3... the two characters '0' and 'a'
sha256(bytes([0x0a])) // 01ba4719... the single byte 0x0A
// Which one is right? Whichever the other system did - and the only
// way to find out is to ask it, not to try both and hope.The hex mode also exists for bytes that text cannot express. A NUL byte, a byte above 127 that is not part of a character, the first few bytes of a PNG — none of them survive a round trip through a text field, and all of them can be pasted as hex. Spaces and colons are ignored, so a digest copied out of another tool works as it stands.
SHA-1 is here, and should not be used for anything new
SHA-1 has a demonstrated collision attack: two different files can be made to share a digest, and doing it takes minutes on a laptop. That is a genuine break of the property a hash is used for, and it is why SHA-1 was withdrawn for certificates and for Git's object format.
It is offered anyway, and labelled, because it is still everywhere in things you have to interoperate with: an old package manifest, a vendor file format, a database's built-in function. Sometimes you need to reproduce the value a system computed years ago. Use it for that. Do not choose it for something you are building, and never treat a SHA-1 match as evidence that a file is the one you expect.
| Algorithm | Output | For |
|---|---|---|
| SHA-256 | 256 bits | The default. Use this unless you have a reason. |
| SHA-384 | 384 bits | The SHA-512 family, truncated. Rarely what you want. |
| SHA-512 | 512 bits | Same family as SHA-384, longer digest. |
| SHA-1 | 160 bits | Reading what an old system stored. Nothing else. |
| CRC-32 | 32 bits | Detecting accidental corruption. Not a security control. |
MD5 is not here, on purpose
The browser's cryptography has no MD5. `crypto.subtle.digest('MD5', ...)` throws, and this page does not paper over that with an implementation of its own. There are two ways to add one and both are worse than the problem.
- Hand-writing it. MD5 is a compression function with a published collision attack, and the failure mode of a hand-written version is silent: it looks correct, it passes every test you write against itself, and it disagrees with a server in production. A subtly wrong MD5 is more dangerous than no MD5, because it is trusted.
- Taking a dependency. It is a real download for every visitor, for an algorithm with no defensible use, on a site whose entire premise is that it does the minimum in your browser.
If you genuinely need an MD5 digest, it is because something already stores one — a legacy system, a third-party integration, a file format you do not control. That is the thing to change, and it will not be changed by a web page.
CRC-32 is a checksum, and the difference is not academic
CRC-32 is in ZIP headers, PNG chunks, gzip trailers and Ethernet frames. It is there to catch a bit flipped in transit or on disk. It has no key, no salt, and no meaningful avalanche behaviour, and finding two different inputs with the same checksum takes microseconds.
crc32("plumless") == crc32("buckeroo")
Two different strings. One checksum. This has been known since the
1990s and is the reason CRC-32 is described as an error-detecting
code and never as a hash.So: use CRC-32 to ask 'did these bytes arrive intact'. Never to ask 'is this the file I was sent', and never as part of anything that is supposed to be tamper-evident. For that you want a keyed construction — an HMAC, or a signature — which this page does not do.
What a hash is not
It is not encryption. There is no key, so there is nothing to decrypt, and the digest does not contain the input in any recoverable sense. Every hash reduces an arbitrary amount of data to a fixed number of bits, so there must be collisions; the only question is how hard they are to find, and for SHA-256 the answer is 'nobody has managed it in twenty years'.
It is not a password store either. A password has low entropy, so an attacker can hash every plausible password and look for a match — which is why passwords are stored with a slow, deliberately expensive function like bcrypt or Argon2 rather than a fast one like SHA-256. Speed is a liability when the input is something a person chose.
Case and prefixes
Hex digests are case-insensitive in practice, and almost every reference gives them in lower case. The uppercase option is there for the systems that insist. The prefixed form is `sha256:` with no hyphen, the way a container image digest and a `go.sum` line both write it — `sha-256:` is a form no real tool emits, and it would be a nuisance to discover that in a Dockerfile.
# a bare digest, what a reference implementation prints
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
# a container image digest, no hyphen
sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
# a go.sum line: the file name, then the digest
main.go ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015adDoing it yourself
In the browser, `crypto.subtle` is the only thing you need, and it is worth using rather than reaching for a library: it is the platform's own implementation, it is what the platform will use anyway, and it is the one place where "runs in the browser" can be true without qualification.
const bytes = new TextEncoder().encode("hello")
const buf = await crypto.subtle.digest("SHA-256", bytes)
const hex = [...new Uint8Array(buf)]
.map(b => b.toString(16).padStart(2, "0"))
.join("")
// `digest` is asynchronous. It is async because the platform may
// move the work off the main thread, and a synchronous version
// does not exist.
// In Node, the same thing without the async dance:
import { createHash } from "node:crypto"
createHash("sha256").update("hello").digest("hex")In every other language the standard library has one, and the only decision worth making is which algorithm. A 32-bit CRC in a build script, SHA-256 for anything addressed by content, an HMAC where a key is involved.
Frequently asked questions
- Why does my hash not match the other system?
- Almost always the encoding. A string has no bytes until something picks them, and UTF-8 is not the only choice. Check what byte count the other side is reading, and whether it is hashing the characters or the bytes your hex value stands for. Those are different inputs and the digests will not match.
- Can you add MD5?
- No, and that is a decision rather than an omission. The browser has no MD5, hand-writing one is dangerous because a wrong implementation looks right, and a dependency for an algorithm nobody should be using is a bad trade. If you need an MD5 it is because something already stores one.
- Is SHA-1 safe to use?
- For reading a value an old system stored, yes — you are reproducing it, not relying on it. For anything new, no: there is a practical collision attack, and it is why SHA-1 was withdrawn for certificates. Use SHA-256.
- Is CRC-32 a hash?
- It is a checksum. It finds accidental corruption, which is what it is for in ZIP files and network frames. It is not tamper-evident and must never be treated as one — 'plumless' and 'buckeroo' have the same CRC-32, and finding pairs like that is trivial.
- Can you reverse a hash?
- No, and that is a property rather than a feature. There is no key and no process; the digest simply does not contain the input. Note the practical caveat: for something a person chose, like a password, an attacker can hash every likely guess and compare — which is why passwords use a deliberately slow function instead.
- Does this send my input anywhere?
- No. It runs in your browser tab using the browser's own cryptography, and nothing is uploaded, stored or logged. Worth being emphatic about here, because a hash is very often taken of a credential or a customer record.