MD5 is one of those algorithms every developer bumps into eventually — in file checksums, cache keys, old database schemas, third-party APIs. It's fast, it's everywhere, and it's also badly misunderstood. Half the arguments I've seen about MD5 come from people using it for the wrong job.

So let's sort out where it belongs and where it'll get you burned.

What MD5 actually is

MD5 (Message Digest 5) is a cryptographic hash function that takes any input and produces a fixed 128-bit output, usually written as a 32-character hexadecimal string. Same input, same output — every time. Change a single byte and the whole hash changes.

md5("hello")  -> 5d41402abc4b2a76b9719d911017c592
md5("Hello")  -> 8b1a9953c4611296a827abf8c47804d7

Notice how flipping one letter from lowercase to uppercase produces a completely different digest. That property — the avalanche effect — is what makes hashes useful for detecting change.

Where MD5 is still fine

Despite its reputation, MD5 is perfectly reasonable for anything that doesn't depend on it being cryptographically secure:

  • Cache keys. Hashing a long query string or a request payload into a short, stable key is a classic use. Collisions are astronomically unlikely for arbitrary inputs, and even if one happened, worst case you serve a stale cache entry.
  • Deduplication. Grouping identical files or records by their MD5 is quick and works well as a first-pass filter.
  • Checksums for accidental corruption. Verifying that a file downloaded correctly, or that a config didn't get mangled in transit. This guards against random bit-flips, not against a motivated attacker.
  • ETags and change detection. Fingerprinting content so you know when to invalidate something downstream.

The common thread: none of these assume an adversary is deliberately crafting inputs to fool you.

Where MD5 will get you in trouble

MD5 is broken for security purposes. This isn't theoretical anymore — practical collision attacks have existed for years, meaning someone can produce two different inputs that hash to the same value on purpose.

Never use MD5 for passwords

This is the big one. Storing password hashes as MD5 — even salted — is a known bad practice. MD5 is fast, and speed is exactly what you don't want for password hashing. An attacker with a modern GPU can try billions of MD5 guesses per second. Use a purpose-built password hash instead: bcrypt, scrypt, or Argon2. They're deliberately slow and tunable.

Never use MD5 for digital signatures or integrity against attackers

If the integrity check needs to survive someone actively trying to forge content — software update packages, signed tokens, certificate fingerprints — MD5 is unacceptable. Reach for SHA-256 or better. The collision attacks mean an attacker could substitute malicious content that still produces the "correct" hash.

Watch out for legacy systems quietly relying on it

I've inherited more than one system where MD5 was used as a security boundary because "it's always been that way." If you find MD5 guarding anything that matters, treat it as tech debt with a security flag on it.

A short worked example

Say you're building a caching layer and want a compact key from a request. Here's the idea in a few languages.

Python:

import hashlib

payload = "/api/products?category=shoes&sort=price"
key = hashlib.md5(payload.encode("utf-8")).hexdigest()
print(key)  # 32-char hex string, safe to use as a cache key

Node.js:

const crypto = require("crypto");

const payload = "/api/products?category=shoes&sort=price";
const key = crypto.createHash("md5").update(payload).digest("hex");
console.log(key);

PHP:

$payload = "/api/products?category=shoes&sort=price";
$key = md5($payload);
echo $key;

All three produce the same 32-character digest for the same input, which is exactly what you want in a distributed cache — a Node service and a Python worker can compute matching keys.

When you just need to check a value by hand — verifying a checksum from a download page, or confirming your code produces the digest you expect — it's quicker to paste the string into an MD5 Hash Generator than to open a REPL. Handy for debugging when a checksum mismatch has you second-guessing your own code.

Common gotchas

  • Encoding matters. Hashing a string means hashing its bytes. If one system encodes as UTF-8 and another as UTF-16, you'll get different hashes for the "same" text. Always be explicit about encoding.
  • Trailing whitespace and line endings. A file with \r\n line endings hashes differently from one with \n. This trips people up constantly when comparing checksums across Windows and Unix.
  • Case of the hex output. Some tools output uppercase hex, others lowercase. They represent the same value, but a naive string comparison will fail. Normalise before comparing.
  • Salting doesn't rescue MD5 for passwords. Salt prevents rainbow-table reuse, but it does nothing about MD5's raw speed. Don't confuse the two problems.
  • Truncating the hash. Chopping an MD5 to, say, 8 characters massively increases collision odds. Fine for a low-stakes short id, dangerous if you're assuming uniqueness at scale.

The one-line rule

If the answer to "what happens if someone deliberately crafts a colliding input?" is "nothing bad," MD5 is fine and fast. If the answer is "a security breach," pick something else — SHA-256 for integrity, Argon2 or bcrypt for passwords.

MD5 isn't evil. It's a tool that got asked to do a job it was never designed for. Keep it in the non-security corner of your toolbox and it'll serve you well for years.

Photo by Rahul Mishra on Unsplash