Gmail and many providers route user+tag@gmail.com to the same mailbox as user@gmail.com. Without stripping plus-tags before deduplication, a single subscriber can appear multiple times with different tags (user+promo@, user+news@, user+test@) — each receiving its own copy of every send. The duplication inflates list counts, skews per-contact engagement metrics, and in markets with per-contact billing (most ESPs), directly increases cost. More critically, a suppression or unsubscribe recorded against the base address won't suppress the tagged variants, creating compliance exposure under CAN-SPAM § 5's unsubscribe-honoring requirement.
Low because plus-address collisions affect a minority of subscribers, but each duplicated contact creates a gap in unsubscribe coverage that compounds across campaigns.
Strip the plus-tag from the local part during canonicalization before the deduplication lookup:
function canonicalizeEmail(email: string): string {
const [local, domain] = email.toLowerCase().trim().split('@')
if (!local || !domain) return email.toLowerCase().trim()
return `${local.split('+')[0]}@${domain}`
}
const canonical = canonicalizeEmail(rawEmail)
await db.contact.upsert({
where: { email: canonical },
create: { email: canonical, email_original: rawEmail },
update: { updated_at: new Date() }
})
Store both email (canonical) and email_original (as submitted). Suppressions and unsubscribes must be keyed on the canonical form so all variants are covered. Note: Outlook's plus-addressing behavior differs from Gmail — plus-stripping is safe for @gmail.com and @yahoo.com domains; apply universally with awareness that it is a dedup heuristic, not a routing guarantee.
ID: data-quality-list-hygiene.dedup-normalization.plus-address-normalization
Severity: low
What to look for: Enumerate all email normalization steps applied before deduplication. Check whether the system strips the plus-tag from email addresses before deduplication checks. alice+promo@gmail.com and alice@gmail.com refer to the same mailbox. Without stripping the tag, a user can subscribe with multiple plus-tagged variants and receive duplicate sends. Look for regex or string manipulation that removes +... from the local-part before lookup.
Pass criteria: Plus-tags are stripped from the local-part for deduplication purposes. Optionally: the canonical (stripped) form is stored while the original is kept in a separate column for reference. At least 1 normalization function handles plus-tag stripping.
Fail criteria: Plus-tagged addresses are treated as unique addresses. Multiple +tag variants for the same base address can co-exist in the list.
Skip (N/A) when: The system explicitly uses plus-tagging for contact attribution and intentionally tracks per-tag variants.
Detail on fail: Example: "user+campaign1@gmail.com and user+campaign2@gmail.com treated as separate contacts — same person receives duplicate sends"
Remediation: Strip plus-tags before deduplication lookup:
function canonicalizeEmail(email: string): string {
const [local, domain] = email.toLowerCase().trim().split('@')
if (!local || !domain) return email.toLowerCase().trim()
// Strip plus-tag from local part
const canonicalLocal = local.split('+')[0]
return `${canonicalLocal}@${domain}`
}
// Store canonical form; optionally keep original:
const canonical = canonicalizeEmail(rawEmail)
await db.contact.upsert({
where: { email: canonical },
create: { email: canonical, email_original: rawEmail, ... },
update: { updated_at: new Date() }
})
Note: Gmail and many providers route user+tag@gmail.com to user@gmail.com. However, Outlook's plus-addressing behavior differs — apply this normalization selectively or universally with awareness of false deduplication risk on providers that treat + as a literal character (rare in practice).