emailnorm

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 18, 2026 License: MIT Imports: 3 Imported by: 0

README

emailnorm

Go Reference

Go library for canonicalizing email addresses to prevent duplicate accounts and registration spam.

Why normalization matters

Most web services store email addresses as raw, lowercased strings. Spammers exploit provider-specific routing quirks to register multiple accounts that route into one mailbox:

  • f.o.o.b.a.r@gmail.com
  • foobar+trial1@gmail.com
  • foobar+promo@googlemail.com

To a standard database query, these look like distinct users. To Google, every one of these delivers to foobar@gmail.com.

Naive normalizers often cause serious deliverability bugs by over-stripping:

  • Stripping dots on Google Workspace or Fastmail can collide separate coworkers.
  • Stripping plus signs on Yahoo, Tuta, GMX, Web.de, Naver, QQ Mail, or Brave aliases breaks deliverability because those services reject sub-addressing (550 User not found).
  • Merging Hotmail and Outlook causes collisions because Microsoft allows different people to own the same username across different domains.

emailnorm encodes verified routing rules for major email providers without collapsing distinct mailboxes.

When using emailnorm for user accounts, store both the raw email and the normalized email in separate database columns:

sql CREATE TABLE users ( id UUID PRIMARY KEY, email TEXT NOT NULL, -- Raw email as entered (e.g., Jane.Doe+receipts@googlemail.com) normalized_email TEXT NOT NULL UNIQUE -- Canonical email (e.g., janedoe@gmail.com) );

Why store both?
  • Outbound delivery: Send all transactional and marketing emails to the raw address. Users often configure mail filters based on their aliases (such as +news or +invoices) and expect mail to arrive at the address they entered.
  • User preference: Preserves the user's preferred casing and domain alias in the interface.
  • Deduplication and security: Use ormalized_email for unique constraints and login lookup queries. This prevents malicious or accidental duplicate registrations, multiple trial abuse, and referral fraud.

Supported providers

  • Google (gmail.com, googlemail.com): maps googlemail.com to gmail.com, strips dots, and removes +tag suffixes. Custom domains on Google Workspace preserve dots.
  • Proton (proton.me, protonmail.com, protonmail.ch, pm.me): maps alias domains to proton.me, strips plus tags, and preserves dots.
  • Fastmail (over 100 domains): folds subdomain addressing (alias@username.fastmail.com to username@fastmail.com).
  • Apple iCloud (icloud.com, me.com, mac.com): maps me.com and mac.com to icloud.com, and strips plus tags.
  • Yandex (yandex.ru, ya.ru, and country TLDs): maps regional domains to yandex.ru, strips plus tags, and converts hyphens to dots (first-last to first.last).
  • Microsoft (outlook.com, hotmail.com, live.com, msn.com): strips plus tags while keeping separate domains distinct.
  • Mail.ru (mail.ru, bk.ru, inbox.ru, list.ru, internet.ru): strips plus tags while keeping separate domains distinct.
  • Zoho Mail and DuckDuckGo: strips plus tags and preserves dots.
  • Yahoo, Tuta, GMX, Web.de, Naver, Daum, QQ, NetEase, and Brave: keeps literal addresses without plus stripping so outgoing mail does not bounce.
  • Internationalized domains: normalizes Unicode to NFC and converts non-ASCII domain names to Punycode.

Installation

go get github.com/fumbledlol/emailnorm

Usage

Normalizing email addresses
package main

import (
	"fmt"

	"github.com/fumbledlol/emailnorm"
)

func main() {
	// Gmail: removes dots and plus tags, unifies googlemail
	fmt.Println(emailnorm.Normalize("  John.Doe+promo@googlemail.com "))
	// Output: johndoe@gmail.com

	// Proton: maps alias domains to proton.me, removes plus tag
	fmt.Println(emailnorm.Normalize("user+secret@protonmail.ch"))
	// Output: user@proton.me

	// Yandex: maps regional domain, unifies hyphens and dots
	fmt.Println(emailnorm.Normalize("first-last+tag@yandex.kz"))
	// Output: first.last@yandex.ru

	// Fastmail: folds subdomain addressing
	fmt.Println(emailnorm.Normalize("shopping@myaccount.fastmail.com"))
	// Output: myaccount@fastmail.com

	// GMX, Naver, Brave: keeps plus signs to prevent delivery failure
	fmt.Println(emailnorm.Normalize("user+tag@gmx.de"))
	// Output: user+tag@gmx.de

	// Internationalized domain names: converts to ASCII Punycode
	fmt.Println(emailnorm.Normalize("contact@München.de"))
	// Output: contact@xn--mnchen-3ya.de
}
Inspecting provider rules
provider := emailnorm.DetectProvider("user@gmx.de")
fmt.Println(provider)                           // GMX
fmt.Println(provider.SupportsPlusAddressing())   // false
fmt.Println(provider.StripsDots())               // false

gmail := emailnorm.DetectProvider("user@gmail.com")
fmt.Println(gmail.StripsDots())                  // true

Normalization rules

Provider family Strip dots Strip plus tag Domain folding Example input Normalized output
Gmail Yes Yes googlemail.com -> gmail.com f.o.o+bar@googlemail.com foo@gmail.com
Proton No Yes protonmail.*, pm.me -> proton.me user.name+ref@protonmail.com user.name@proton.me
Fastmail No Yes Subdomain fold (*@user.<dom> -> user@<dom>) newsletter@john.fastmail.com john@fastmail.com
iCloud No Yes me.com, mac.com -> icloud.com user+tag@me.com user@icloud.com
Yandex No (- -> .) Yes ya.ru, yandex.* -> yandex.ru first-last+tag@ya.ru first.last@yandex.ru
Outlook / Hotmail No Yes Distinct domains john.doe+trial@outlook.com john.doe@outlook.com
Mail.ru No Yes Distinct domains user+tag@mail.ru user@mail.ru
Zoho Mail No Yes Distinct domains user+tag@zoho.com user@zoho.com
DuckDuckGo No Yes Distinct domains user+tag@duck.com user@duck.com
Brave aliases No No Literal routing user+tag@bravealias.com user+tag@bravealias.com
Yahoo No No Literal routing user+tag@yahoo.com user+tag@yahoo.com
Tuta No No Literal routing user+tag@tutanota.com user+tag@tutanota.com
GMX / Web.de No No Literal routing user+tag@gmx.de user+tag@gmx.de
Naver / Daum No No Literal routing user+tag@naver.com user+tag@naver.com
QQ / NetEase No No Literal routing 123456+tag@qq.com 123456+tag@qq.com
Custom / Generic No Yes Literal domain dev+test@example.org dev@example.org

Benchmarks

BenchmarkNormalize_Gmail-12       2113540    565.2 ns/op    112 B/op    4 allocs/op
BenchmarkNormalize_Proton-12      3206744    380.1 ns/op     56 B/op    2 allocs/op
BenchmarkNormalize_Fastmail-12    2263084    540.5 ns/op     72 B/op    3 allocs/op
BenchmarkNormalize_Yandex-12      3018289    407.6 ns/op     72 B/op    3 allocs/op

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Normalize

func Normalize(email string) string

Normalize returns the canonical abuse-prevention form of an email address. It normalizes Unicode (NFC and IDNA ASCII), applies provider-specific domain folding, and strips tags or dots only where supported by verified provider behavior. Invalid inputs without exactly one "@" are returned trimmed and lowercased.

Types

type Provider

type Provider int

Provider identifies the detected email service provider family.

const (
	ProviderUnknown Provider = iota
	ProviderGmail
	ProviderProton
	ProviderFastmail
	ProviderICloud
	ProviderYahoo
	ProviderOutlook
	ProviderYandex
	ProviderTuta
	ProviderMailRu
	ProviderGMX
	ProviderWebDe
	ProviderNaver
	ProviderDaum
	ProviderQQ
	ProviderNetEase
	ProviderZoho
	ProviderDuckDuckGo
	ProviderBrave
)

func DetectProvider

func DetectProvider(emailOrDomain string) Provider

DetectProvider returns the detected major provider for a given domain or email address.

func (Provider) String

func (p Provider) String() string

func (Provider) StripsDots

func (p Provider) StripsDots() bool

StripsDots reports whether the provider ignores dots within the local part.

func (Provider) SupportsPlusAddressing

func (p Provider) SupportsPlusAddressing() bool

SupportsPlusAddressing reports whether the provider officially routes '+tag' sub-addresses to the base mailbox. Providers that bounce or reject sub-addressing return false.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL