Plus-addressing normalization
Why it matters
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.
Severity rationale
Low because plus-address collisions affect a minority of subscribers, but each duplicated contact creates a gap in unsubscribe coverage that compounds across campaigns.
Remediation
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.
Detection
-
ID:
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.comandalice@gmail.comrefer 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
+tagvariants 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.comtouser@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).
External references
- iso-25010:2011 · functional-correctness — Functional Correctness (functional suitability)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from data-quality-list-hygiene·automated