The short answer: store a canonical version of every email (no +tag, and for Gmail no dots) and every phone number (E.164), make both unique, and screen new signups for throwaway inboxes. Those three steps stop the large majority of multi-accounting.
The tricks
| Trick | Example | Looks like |
|---|---|---|
| Plus addressing | jane+1@acme.com, jane+2@acme.com |
Different addresses |
| Gmail dots | j.ane@gmail.com, ja.ne@gmail.com |
Different addresses |
| Googlemail | jane@googlemail.com |
A different domain |
| Throwaway inboxes | x1@mailinator.com, x2@mailinator.com |
Unlimited addresses |
| Phone formatting | 07400 123456, +44 7400 123456 |
Different numbers |
All of the first four Gmail variations land in the same inbox.
Store canonical values
Veqen returns canonical for every email and e164 for every phone. Store both, and add unique indexes:
ALTER TABLE users ADD COLUMN canonical_email text;
ALTER TABLE users ADD COLUMN phone_e164 text;
CREATE UNIQUE INDEX users_canonical_email ON users (canonical_email);
CREATE UNIQUE INDEX users_phone_e164 ON users (phone_e164) WHERE phone_e164 IS NOT NULL;
check = requests.post(
"https://veqen-app.com/v1/signup/check",
headers={"Authorization": f"Bearer {os.environ['VEQEN_API_KEY']}"},
json={"email": email, "phone": phone, "ip": ip},
timeout=4,
).json()
canonical = check["email"]["canonical"]
e164 = check["phone"]["e164"] if check.get("phone") and check["phone"]["valid"] else None
if User.objects.filter(canonical_email=canonical).exists():
return form_error("You already have an account. Sign in instead.")
const check = await fetch("https://veqen-app.com/v1/signup/check", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.VEQEN_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ email, phone, ip }),
}).then((r) => r.json());
const canonical = check.email.canonical;
if (await db.user.findUnique({ where: { canonicalEmail: canonical } })) {
return formError("You already have an account. Sign in instead.");
}
Keep the address the person typed for sending email; use the canonical one only for uniqueness.
Then screen the rest
Canonical matching stops variations of one inbox. Throwaway services give people unlimited real inboxes, so also refuse disposable addresses (see blocking disposable emails).
Frequently asked questions
Is removing dots safe for every domain?
No, only Gmail and Googlemail ignore dots. Veqen removes dots only for those, and strips +tags for all domains.
What about Outlook or iCloud aliases?
Those are separate, real addresses the person created in their account. They can't be generated on the fly like +tags, so they're far less useful for abuse.