How to Normalize Plus-Addressed Emails at Signup
by EmailGuard Engineering
Share this article
Enjoyed this article?
More notes on building products, infrastructure, and teams.
by EmailGuard Engineering
More notes on building products, infrastructure, and teams.
Plus addressing (user+tag@domain) and provider alias rules (Gmail ignoring dots) let one inbox register as many database rows. Normalize at signup: store the original address for delivery and support, and enforce uniqueness on a canonical normalized form. EmailGuard returns both fields—plus a subaddressing flag—from detect email characteristics.
This is the product how-to companion to the email normalization and subaddressing knowledge base articles. For the full signal checklist, see the email validation API guide.
These inputs often deliver to the same Gmail inbox:
| Submitted email | Same inbox? |
|---|---|
jane.doe@gmail.com | baseline |
janedoe@gmail.com | yes (Gmail ignores dots) |
jane.doe+app@gmail.com | yes (plus tag stripped for delivery) |
Jane.Doe+promo@gmail.com | yes (case + tag + dots) |
Without a canonical key, your UNIQUE(email) constraint treats each as a different user. Abuse and “second free trial” flows exploit that. Support then sees multiple accounts for one person.
Plus addressing itself is legitimate—users tag filters with +newsletter or +shopping. The goal is dedupe, not a blanket ban on +.
Hand-rolling every provider rule is fragile. Prefer an API that returns a maintained normalized string.
| Field | Use at signup |
|---|---|
email | As submitted—keep for display, outbound mail, and audit |
normalized | Unique index / duplicate check |
subaddressing | Analytics or optional policy (allow tags vs require bare local part) |
syntax_validation | Reject garbage before normalize |
disposable | Still block throwaways (signup guide) |
Example response for a tagged Gmail address:
{
"data": {
"email": "jane.doe+newsletter@gmail.com",
"syntax_validation": true,
"normalized": "janedoe@gmail.com",
"subaddressing": true,
"public_domain": true,
"disposable": false
}
}Corporate domains typically get light canonicalization (lowercase domain) without aggressive alias collapsing—see normalization KB.
email:detect).syntax_validation is false → reject.disposable is true → reject (or your product policy).normalized.email = original, email_normalized = normalized, optionally subaddressing = flag.import { createPublicClient, loadConfig } from "@emailguard/sdk";
const client = createPublicClient(loadConfig());
async function register(email: string, createUser: (row: {
email: string;
emailNormalized: string;
subaddressing: boolean;
}) => Promise<void>) {
const data = await client.detectEmailCharacteristics({ email });
if (!data?.syntax_validation) {
throw new Error("Enter a valid email address.");
}
if (data.disposable) {
throw new Error("Use a permanent email address.");
}
const normalized = data.normalized ?? email.trim().toLowerCase();
// SELECT id FROM users WHERE email_normalized = $normalized
// if found → duplicate handling
await createUser({
email: data.email ?? email,
emailNormalized: normalized,
subaddressing: Boolean(data.subaddressing),
});
}SDK overview: EmailGuard SDK guide. Try the free email normalizer for a quick manual check.
subaddressing| Product need | Policy |
|---|---|
| One account per person (consumer / freemium) | Allow signup; unique on normalized |
| Allow filter tags | Allow; store subaddressing for analytics |
| Strict “no aliases” | Reject when subaddressing is true (rare—hurts Gmail power users) |
| Abuse focus | Alias alone is weak; combine with disposable, velocity, device signals |
Do not treat subaddressing like disposable. A tagged Gmail is usually a real user. A tagged disposable domain is a stronger abuse signal—check both flags.
email only — the classic duplicate-account hole.+ for every domain — safe as a heuristic for many hosts, wrong as a universal RFC rule; prefer provider-aware normalized.Usually no. Block disposables; dedupe on normalized. Restrict aliases only when your risk model requires one local-part form.
No. Normalization collapses aliases of the same mailbox. Disposable detection classifies throwaway domains. Use both—see blocklist vs API.
googlemail.com?Treat it as Gmail’s domain alias. EmailGuard’s normalization maps known consumer aliases into a canonical host where applicable (KB details).
You can strip +tag locally as a first pass. You will miss Gmail dots, host aliases, and provider-specific rules unless you maintain that matrix yourself. The detect API exists so you don’t.
email_normalized to your users table and backfill carefully.