Email Hashing for Identity Partners
Below is a comprehensive, publisher-friendly guide showing how to encode hashed email addresses (hem's) for identity platforms and ad tech integrations.
Where to find email addresses
Publishers typically collect user email addresses at these common touchpoints:
Newsletter sign-up forms
When users subscribe to your newsletter, you capture their email directly from the form submission. This is often the first interaction point where you can collect and hash the email for identity purposes.
// Example: Capture from any newsletter form (resilient to dynamic content)
document.addEventListener('submit', async function(e) {
// Look for forms with email inputs
const emailInput = e.target.querySelector('input[type="email"]');
if (emailInput && emailInput.value) {
const hashedEmail = await hashEmail(emailInput.value);
// Store or use the hashed email
}
});
Login events
During user authentication (login/register), you have access to the user's email address. This is an ideal time to hash and store the email for consistent user identification across sessions.
Newsletter click tracking
When users click through from email newsletters to your site, you can often capture their email from URL parameters or tracking pixels, allowing you to maintain identity continuity from email to web. Consult with your email service provider (ESP) about enabling email pass-through or tracking parameters.
Pro tip
Set up your hashing function early in your site's lifecycle, so you can consistently hash emails whenever they're encountered, ensuring a unified approach to user identity.
1. Why hash an email?
- Privacy – the real address stays hidden; you store or pass only a fixed-length, irreversible code.
- Consistency – the same email always produces the same hash, so you can match users without exposing them.
- Industry standard – ad, analytics, and identity platforms expect SHA-256 hashed emails.
2. Email Normalization - Always Required
Critical First Step: Normalize Before Hashing
Always normalize emails before hashing - this ensures consistent matching across all platforms and maximizes identity resolution. Even small variations in user input can completely break identity matching if not normalized properly.
Our hashing function automatically applies the correct normalization rules:
For All Email Addresses:
- Trim spaces:
" Alice@Example.COM "➜"Alice@Example.COM" - Lowercase:
"Alice@Example.COM"➜"alice@example.com"
For Gmail Addresses (automatically detected):
- Remove dots from username:
"jane.doe@gmail.com"➜"janedoe@gmail.com" - Remove plus aliases:
"janedoe+newsletter@gmail.com"➜"janedoe@gmail.com"
Why This Matters
Without proper normalization:
"User@Example.com"and"user@example.com"produce different hashes- Gmail users entering
jane.doe@gmail.comvsjanedoe@gmail.comwon't match - Targeting and frequency capping breaks across seemingly identical users
Important: Gmail normalization means these emails produce the same hash:
jane.doe@gmail.comjanedoe@gmail.comjane.doe+work@gmail.com
3. SHA-256 Hash Function
<script>
/**
* Hash an email using SHA-256 with automatic normalization.
* Automatically detects Gmail addresses and applies appropriate rules.
* Returns lowercase hexadecimal string (64 characters).
*/
async function hashEmail(email = '') {
// Handle empty/null/undefined cases
if (!email) return '';
// Always apply basic normalization first
email = String(email).trim().toLowerCase();
// Gmail-specific normalization (automatically detected)
if (email.endsWith('@gmail.com')) {
let [username, domain] = email.split('@');
// Remove dots from username
username = username.replace(/\./g, '');
// Remove plus aliases
const plusIndex = username.indexOf('+');
if (plusIndex !== -1) {
username = username.slice(0, plusIndex);
}
email = `${username}@${domain}`;
}
// Convert to bytes and hash with SHA-256
const data = new TextEncoder().encode(email);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// Convert to lowercase hexadecimal string
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
// Example uses
hashEmail(' User@Example.COM ').then(console.log);
// → b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514
hashEmail(' Jane.Doe+Work@Gmail.COM ').then(console.log);
// → d6117306485ed0e50afab3ac871e98f81699151f30281527d63ff5f233656c69
</script>
4. Testing Your Implementation
Normalization Test Cases
| Original Email | Normalized Result | Why |
|---|---|---|
Jane.Doe+Work@Gmail.com |
janedoe@gmail.com |
Gmail: removed dots, plus alias, lowercased |
user@EXAMPLE.com |
user@example.com |
Standard: trimmed, lowercased |
test+alias@yahoo.com |
test+alias@yahoo.com |
Non-Gmail: only basic normalization |
Expected SHA-256 Hash Outputs
For user@example.com:
- SHA-256:
b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514
For jane.doe+work@gmail.com (normalized to janedoe@gmail.com):
- SHA-256:
d6117306485ed0e50afab3ac871e98f81699151f30281527d63ff5f233656c69
5. Using hashed emails with our wrapper
Once you have your hashed email, you can pass it to our wrapper to improve ad targeting and user identification. This helps identity partners better match users across platforms while maintaining privacy.
Setting the user identity profile
Call the setIdProfile function as early as possible, ideally before making any ad requests. This ensures the hashed email is available for all subsequent ad calls:
window.tude = window.tude || { cmd: [] };
window.tude.cmd.push(function() {
window.tude.setIdProfile({
e: 'your-sha256-hashed-email-here', // SHA-256 hash of user email address
});
});
Complete integration example
// Hash email and set up identity profile
async function setupIdentity(userEmail) {
const hashedEmail = await hashEmail(userEmail);
window.tude = window.tude || { cmd: [] };
window.tude.cmd.push(function() {
window.tude.setIdProfile({ e: hashedEmail });
});
}
// Use it when you capture an email
document.getElementById('newsletter-form').addEventListener('submit', async function(e) {
const email = document.getElementById('email-input').value;
await setupIdentity(email);
// Email identity is now set for all future ad requests
});
Pro tips
- Timing matters: Call
setIdProfileas early as possible, ideally right after user login or email capture - Don't block ad requests: If email collection is async (like newsletter signups), don't delay your first ad auction waiting for the email. Run the first auction immediately, then set the identity profile for subsequent auctions
- Before ad requests: Always ensure the identity profile is set before requesting ads (when email is available)
- Consistent hashing: Use the same hashing method across your entire site for consistency
- Privacy first: Only the hashed version is sent - the original email never leaves your domain
Performance note
First auction priority: Your first ad auction should never be delayed waiting for email data. If a user signs up for your newsletter after page load, that's great for future auctions, but don't sacrifice the performance of your initial ad request.
// ✅ Good: Run first auction immediately
window.tude = window.tude || { cmd: [] };
window.tude.cmd.push(function() {
window.tude.refreshAdsViaDivMappings([
{
divId: 'ad-slot-1',
baseDivId: 'pb-slot-incontent-1'
}
]);
});
// Later, when newsletter signup happens...
document.getElementById('newsletter-form').addEventListener('submit', async function(e) {
const email = document.getElementById('email-input').value;
const hashedEmail = await hashEmail(email);
// Set identity for future auctions
window.tude.cmd.push(function() {
window.tude.setIdProfile({ e: hashedEmail });
});
// Future ad requests will now benefit from the email data
});
6. Common Questions
| Question | Answer |
|---|---|
| Can anyone get the original email from the hash? | No. SHA-256 is a one-way hashing function. |
| Why does Gmail get special treatment? | Gmail allows dots and plus aliases that represent the same inbox, so we normalize them for consistent matching. |
| What happens if I don't normalize? | Different variations of the same email produce different hashes, reducing identity matching effectiveness. |
| How do I test my implementation? | Use the test cases in section 4 to verify your normalization and hashing logic. |
7. CryptoJS Alternative (Legacy Support)
If you need to support older browsers, you can use the CryptoJS library:
<!-- Add CryptoJS from CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"
integrity="sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
function hashEmail(email = '') {
// Handle empty/null/undefined cases
if (!email) return '';
// Always apply basic normalization first
email = String(email).trim().toLowerCase();
// Gmail-specific normalization (automatically detected)
if (email.endsWith('@gmail.com')) {
let [username, domain] = email.split('@');
// Remove dots from username
username = username.replaceAll('.', '');
// Remove plus aliases
const plusIndex = username.indexOf('+');
if (plusIndex !== -1) {
username = username.slice(0, plusIndex);
}
email = `${username}@${domain}`;
}
// Hash with SHA-256 and return hex string
return CryptoJS.SHA256(email).toString(CryptoJS.enc.Hex);
}
</script>
You're done 🎉
Quick Start Checklist:
- ✅ Copy the hash function - Use the SHA-256 function from section 3
- ✅ Test with sample data - Verify outputs match expected values
- ✅ Integrate with wrapper - Set up
setIdProfilecalls - ✅ Consistent implementation - Use the same function across your entire site
That's it! The function automatically handles all normalization (basic for all emails, Gmail-specific rules for Gmail addresses) and returns a standard SHA-256 hash that works with all identity platforms.