Email Hashing for Ad Platforms (SHA-256)
Why Hashing, Not Encryption
Ad platforms need a way to match your first-party email records against their own logged-in user base without you ever transmitting plaintext PII and without them ever being able to reverse the value back to the original email. A cryptographic hash function like SHA-256 is one-way by design: it's computationally infeasible to recover the input from the output, but the same input always deterministically produces the same output — which is exactly the property needed for exact-match joins without exposing the underlying data.
This is different from encryption, which is explicitly designed to be reversible by someone holding the right key. Hashing for match purposes is deliberately irreversible — nobody is supposed to get the plaintext back, including the platform receiving the hash.
SHA-256 Is the De Facto Standard, With One Caveat
Google, Meta, and most major DSPs/ad platforms standardize on SHA-256 for PII hashing in Customer Match-style products and Conversions API/Enhanced Conversions integrations. SHA-256 is cryptographically strong for this purpose at the time of writing, but hashing alone doesn't protect against a dictionary attack on a known, enumerable space — email addresses aren't random high-entropy strings; an attacker with access to a hash can brute-force it against a precomputed table of hashes for every email in a known leaked database or common name/domain combination, since there's no salt involved in these platform-specified schemes (adding your own salt would break interoperability, since both sides need to independently compute the identical hash).
Hashed Doesn't Mean Anonymous
import { createHash } from "crypto";
function sha256Hex(input: string): string {
return createHash("sha256").update(input, "utf8").digest("hex");
}
function prepareUserData(rawEmail: string, rawPhone: string) {
return {
em: [sha256Hex(rawEmail.trim().toLowerCase())],
ph: [sha256Hex(rawPhone.replace(/[^0-9]/g, ""))],
};
}
// Sent to Meta's Conversions API; "em"/"ph" are already hashed,
// raw PII never leaves your server.
const capiEvent = {
event_name: "Purchase",
user_data: prepareUserData("user@example.com", "+1 415 555 1234"),
custom_data: { value: 59.99, currency: "USD" },
};Common Hashing Mistakes and Their Effect
| Mistake | Effect on Match Rate |
|---|---|
| Not lowercasing before hash | Silent, severe drop — case-sensitive hash mismatch |
| Leaving whitespace in the string | Silent, severe drop |
| Hashing as UTF-16 instead of UTF-8 | Total mismatch, platform-dependent encoding assumption violated |
| Applying your own salt | Total mismatch — platform can't reproduce your salted hash |
| Hashing an already-invalid/malformed email | Correctly produces no match — not a bug, just unmatchable input |
Encoding Consistency Is an Easy Thing to Get Wrong
What's Next?
Hashing is the mechanical layer underneath first-party identity solutions — the next step is understanding how networks like UID2 and RampID build durable, shareable identity graphs on top of exactly this kind of hashed match key.
I build these systems professionally.
Whether it's a RAG pipeline, analytics migration, or AI workflow — let's talk.