What a hash is for — and what it is not for
A hash is a one-way fingerprint. It answers "are these two things byte-identical?" without needing to compare them directly, and "has this file changed since I last saw it?" without keeping a copy. It does not encrypt anything — there is no key and no way back — and it is not, on its own, a password storage mechanism. Those three distinctions cover most of the trouble people get into with hashing.
The properties that make a hash cryptographic are collision resistance (nobody can find two inputs with the same digest), preimage resistance (given a digest, nobody can find an input producing it), and the avalanche effect (a one-bit change scrambles the output). MD5 and SHA-1 have lost the first property completely. They retain preimage resistance in practice, which is why they still serve as non-adversarial checksums, but any use where an attacker chooses the input is broken.
| Algorithm | Digest | Status | Use it for |
|---|---|---|---|
| MD5 | 128 bit / 32 hex | Collisions trivial since 2004 | Comparing files you generated yourself. ETags. Cache keys. |
| SHA-1 | 160 bit / 40 hex | Collisions demonstrated 2017 (SHAttered) | Legacy git object IDs, old TLS fingerprints. Nothing new. |
| SHA-256 | 256 bit / 64 hex | Secure | The default. Integrity, signatures, certificate fingerprints, HMAC. |
| SHA-384 | 384 bit / 96 hex | Secure | TLS cipher suites, some government profiles. SHA-512 truncated. |
| SHA-512 | 512 bit / 128 hex | Secure | Faster than SHA-256 on 64-bit CPUs. Long-lived archival integrity. |
Why MD5 is here at all
The Web Crypto API deliberately does not implement MD5 — the standards body considered it harmful enough to leave out. This page computes it with a local pure-JavaScript implementation instead, the same one behind the htpasswd generator's $apr1$ support. It is included because MD5 remains genuinely useful for non-adversarial work: verifying that a large file copied correctly, matching an S3 ETag, deduplicating your own content, or checking a vendor checksum against a download.
Read the full explanation of hash generator results ↓Show less ↑
What it must never be used for is any decision where someone else supplied the input. Chosen-prefix collisions mean an attacker can produce two files with identical MD5s and completely different behaviour — a benign installer and a malicious one that both "verify". If the checksum exists to defend against tampering rather than against corruption, use SHA-256.
Encoding: why the same text can hash differently
A hash function takes bytes, not characters. This tool encodes your input as UTF-8 first, which is why café (5 bytes — the é is two) hashes differently from the same word in Latin-1 (4 bytes). Almost every modern system uses UTF-8, but if a digest computed elsewhere does not match one computed here, encoding is the first thing to check, followed by trailing whitespace and line endings.
Line endings are the quietest source of mismatches. A file written on Windows ends lines with CRLF; the same file checked out on Linux may have LF. The content looks identical in every editor and the digests differ. Similarly, echo "text" appends a newline while printf "text" does not — which is why the command-line examples below use printf or echo -n.
Hex and Base64 are two renderings of the same digest bytes, not two different hashes. A SHA-256 digest is 32 bytes: 64 hex characters, or 44 Base64 characters including padding. Different ecosystems prefer different renderings — sha256sum gives hex, Subresource Integrity attributes and Docker manifests use Base64 or prefixed hex. Convert between them with the Base64 tool if something expects the form you do not have.
The command-line equivalents
Every digest on this page has an exact shell equivalent. Note the -n on echo — without it you are hashing your text plus a trailing newline, and the digests will not match.
# Text, no trailing newline
printf 'hello' | openssl dgst -sha256
# -> SHA2-256(stdin)= 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
printf 'hello' | shasum -a 256 # macOS / any Perl-equipped system
printf 'hello' | sha256sum # GNU coreutils
# Base64 rather than hex — same bytes, different rendering
printf 'hello' | openssl dgst -binary -sha256 | openssl base64
# MD5, still fine for non-adversarial checksums
printf 'hello' | md5sum # Linux
printf 'hello' | md5 # macOS/BSD
# Whole files
sha256sum ubuntu-24.04.iso
shasum -a 256 -c SHA256SUMS # verify a published checksum list
# A TLS certificate fingerprint, which is a hash of the DER encoding
openssl x509 -in cert.pem -noout -fingerprint -sha256
# HMAC — a keyed hash, which is what you want for authenticating a webhook
printf 'payload' | openssl dgst -sha256 -hmac 'shared-secret'That last one matters more than it looks. A bare hash of a payload proves nothing about who produced it, because anyone can compute a hash. HMAC mixes in a shared secret, so only someone holding the key could have produced the digest. If you are validating incoming webhooks by hashing the body and comparing, you need HMAC, not SHA-256 alone — and the comparison must be constant-time to avoid leaking the expected value through timing.
Where you will meet these in day-to-day operations
- Certificate fingerprints. A SHA-256 fingerprint identifies a specific certificate. Handy for confirming that the certificate a server actually presents is the one you installed — check what is being served with the SSL certificate checker.
- DKIM. The signature covers a hash of the message body plus selected headers. When a mailing list appends a footer, the body hash no longer matches and verification fails with
dkim=fail (body hash did not verify)— see the DKIM checker. - DNSSEC. DS records published at the parent zone are digests of the child's DNSKEY. A DS that does not match any current key breaks the chain of trust; the DNSSEC checker validates that linkage.
- Subresource Integrity.
integrity="sha384-…"on a script tag is a Base64 digest the browser recomputes before executing the file, so a compromised CDN cannot silently swap it. - Git. Object IDs are SHA-1 (with a collision-detection hardening patch). This is why git is described as content-addressable — the name of an object *is* its hash.
- Deduplication and cache keys. Any place you need a short stable identifier for a blob of content. MD5 is entirely adequate here and faster than the SHA family.
Reading a mismatch
When a checksum does not match, work through the causes in order of likelihood. A truncated or still-downloading file is by far the most common — compare byte counts before anything else. Then encoding and line endings. Then a trailing newline from echo. Then the wrong algorithm entirely: a 32-hex-character digest is MD5, 40 is SHA-1, 64 is SHA-256; if you are comparing strings of different lengths you are comparing different algorithms. Only after all of those should you consider actual tampering — and if the file came over HTTPS from a source you trust, tampering is genuinely rare compared with a transfer that stopped early.
One thing a mismatch never tells you is *what* changed. Hashes are all-or-nothing by design; the avalanche effect means a single flipped bit and a completely different file produce equally unrelated digests. If you need to know what differs, you need the original, and diff.