Skip to content
Muhammad Moeed

Email Plus Addressing in SaaS: How to Handle + Aliases Safely

Email validation without restrictive regex: valid special characters, safe email normalization, canonical addresses, and SaaS trial abuse prevention.

An address such as alex+news@example.com is a normal email address. The +news part is called a tag. Many people use tags to sort email, see where an address was shared, or keep test accounts separate.

This can create a problem for SaaS products. A person may sign up for a free trial with alex@example.com, then try again with alex+trial2@example.com.

It is tempting to block the + character during signup. Do not do that. It blocks real users, breaks a useful email feature, and does not stop serious trial abuse.

The better approach is simple: accept valid email addresses, verify who controls the mailbox, and protect the actual benefit with real anti-abuse checks.

What plus addressing means

Every email address has two parts:

local-part@domain

For this address:

alex+invoices@example.com
\___/ \______/ \_________/
 user   tag       domain

alex is the user part. invoices is the tag. The whole left side, alex+invoices, is still the local part of the email address.

RFC 5322, the main Internet email format standard, allows + in an ordinary unquoted local part. So + is not a bad character or a broken email address.

RFC 5233 describes sub-addressing for email filtering. It uses the words user and detail for the two pieces around a separator such as +.

There is one important detail: the RFC allows the syntax, but it does not force every email provider to treat alex+tag@example.com as alex@example.com. The receiving mail provider decides how delivery works.

For example:

  • Google says email sent to Gmail plus variations arrives in the same inbox.
  • Exchange Online has plus addressing enabled by default, but an administrator can turn it off.
  • Apple provides normal aliases and Hide My Email addresses. These are separate addresses that forward to the user.

This is why a SaaS product must not assume that every domain uses the same rules.

Why real users use aliases

Plus aliases are useful, not suspicious by default.

sam+receipts@gmail.com
sam+product-updates@gmail.com
qa+password-reset@company.test

People use them to:

  • put receipts and newsletters into different folders;
  • find out which service leaked or shared an address;
  • test signup, invites, and password resets;
  • keep a separate address for each service;
  • avoid sharing their main address through an alias or relay.

Developers and technical buyers use aliases often. If a developer tool rejects +, it can make a product feel less reliable before the user even sees the product.

Why blocking + does not stop abuse

Suppose a product gives one free trial per email address. Its database checks only the exact email string.

alex@example.com
alex+trial1@example.com
alex+trial2@example.com

Those are three different strings. A basic unique index accepts all three.

Blocking + seems like a fast fix. But the same person can still use many other methods:

  • Gmail dot variations such as a.lex@gmail.com;
  • a catch-all domain that accepts any local part;
  • temporary email services;
  • Apple Hide My Email or another relay service;
  • another mailbox;
  • an organization domain with real, valid aliases.

Google documents that dots do not change a consumer gmail.com address. It also documents that dots can matter for Google Workspace usernames. This means a rule such as “remove every dot for every Google domain” can join two different people by mistake.

The main lesson is that an email string is not proof of a person. Blocking one character only changes the spelling an abuser uses.

Keep development and production rules close

Plus aliases are very useful in development and staging. A QA engineer can test many cases with one inbox:

qa+new-user@example.test
qa+expired-invite@example.test
qa+acme-workspace@example.test

Do not make production email validation stricter than staging validation. That creates two different systems and lets bugs reach production.

Use the same email rules in every environment. Then make test accounts safe in other ways:

  • allow known test domains only in non-production;
  • mark test accounts with a field such as account_type = 'test';
  • do not send test users into production analytics or marketing tools;
  • skip billing only for approved test accounts;
  • use controlled test fixtures when you test fraud rules.

Email validation with regex: why blocking + is an anti-pattern

The phrase “stop validating email addresses with regex” points to a real limitation: matching a pattern cannot tell you whether someone controls a mailbox. A reasonable format check is still useful for catching mistakes before sending mail. The problem is treating a restrictive regex as either a full email standard or a fraud control.

Many signup forms use a regex like this:

^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$

It rejects +, even though + is allowed by RFC 5322. It also rejects other valid email characters.

Which special characters are valid in email addresses?

Alongside letters and digits, RFC 5322 section 3.2.3 allows these characters in an unquoted local-part atom:

! # $ % & ' * + - / = ? ^ _ ` { | } ~

Dots separate atoms. For this unquoted form, a dot cannot start or end the local part, and two dots cannot appear together. Quoted local parts follow different rules. Allowed syntax does not mean every provider offers every possible mailbox name. RFC 5322

Email validation vs verification

Validation checks whether an address fits the formats your application supports. Verification sends a confirmation link or email OTP to check that the user can receive mail there. Use both: a maintained validator for your supported formats, then a verification flow. Neither step proves that the user has never claimed a trial before.

Do not try to fully implement every RFC email rule in one browser regex. Full email parsing is more complicated than it looks. A practical signup flow should:

  1. do a basic format check for a usable address;
  2. accept reasonable local-part characters, including +;
  3. send a short-lived verification link or code;
  4. make the backend enforce all important business rules.

Client-side validation is only a user experience feature. Anyone can call the signup API without using your form. Trial limits, referral limits, and fraud checks must run on the server.

Store more than one email value

Do not use one email field for everything. Delivery, login, duplicate detection, and fraud checks are different jobs.

This is a useful model:

CREATE TABLE user_emails (
    id                  uuid PRIMARY KEY,
    user_id             uuid NOT NULL REFERENCES users(id),
    delivery_email      text NOT NULL,
    comparison_email    text NOT NULL,
    abuse_identity_key  text,
    verified_at         timestamptz,
    canonicalizer_ver   smallint NOT NULL DEFAULT 1,
    created_at          timestamptz NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX user_emails_comparison_uq
    ON user_emails (comparison_email);

CREATE INDEX user_emails_abuse_identity_idx
    ON user_emails (abuse_identity_key)
    WHERE abuse_identity_key IS NOT NULL;

Here is what each value means:

FieldUse it for
delivery_emailThe verified address where you send email. Keep what the user entered.
comparison_emailA careful value used for account uniqueness.
abuse_identity_keyA provider-aware signal for trial or referral abuse checks. Do not treat it as final proof of identity.
verified_atThe time the user proved they could receive email at this address.
canonicalizer_verThe version of the normalization rules used to create the keys.

In a multi-tenant product, email uniqueness may belong inside a tenant instead of across the full product. One consultant may need to use the same verified email in several customer workspaces.

Email address normalization: what is the best practice?

Normalization means creating a comparison value from an email address. It can help spot known aliases, but it can also cause mistakes.

What is a canonical email address?

A canonical email address is a comparison form chosen by an application under a defined set of rules. There is no universal rule that reveals the original mailbox behind every alias. For example, ignoring Gmail dots can help group consumer Gmail addresses, but doing the same for a business domain can join separate mailboxes.

You may call the comparison field normalized_email or comparison_email. Keep it separate from the delivery address and document what its rules mean. In this article, the broader alias grouping goes into abuse_identity_key, which is a risk signal rather than a unique login key.

Why treat email local parts as opaque strings?

Here, “opaque” means your application should preserve the local part without guessing how another provider routes it. RFC 5321 section 2.3.11 assigns interpretation of the local part to the receiving host. It does not prohibit a SaaS product from validating syntax or setting a documented signup policy. It does explain why a sender cannot safely rewrite an unknown provider’s mailbox name. RFC 5321

Exchange Online makes the risk concrete: it first checks the complete address, including the plus tag, before trying the address without the tag. A custom routing setup can therefore behave differently from a simple “remove the tag” assumption. Provider rules can be useful when scoped, tested, and reviewed; detecting a provider from MX records alone is not enough. Microsoft documentation

The safe rule is: only change a local part when you know the provider’s documented behavior. Always keep the original verified delivery address.

This TypeScript example uses conservative rules. It only creates an alias-based abuse signal for consumer Gmail domains. It does not rewrite every domain with a + sign.

import { domainToASCII } from "node:url";

type EmailKeys = {
  deliveryEmail: string;
  comparisonEmail: string;
  abuseIdentityKey?: string;
};

const GMAIL_DOMAINS = new Set(["gmail.com", "googlemail.com"]);

function splitEmail(input: string): [local: string, domain: string] {
  const value = input.trim();
  const at = value.lastIndexOf("@");

  if (at <= 0 || at === value.length - 1) {
    throw new Error("Invalid email address");
  }

  return [value.slice(0, at), value.slice(at + 1)];
}

export function deriveEmailKeys(input: string): EmailKeys {
  const [local, rawDomain] = splitEmail(input);
  const domain = domainToASCII(rawDomain).toLowerCase();

  if (!domain) {
    throw new Error("Invalid email domain");
  }

  // Keep the local part unchanged for normal account comparison.
  const deliveryEmail = `${local}@${domain}`;
  const comparisonEmail = deliveryEmail;

  if (!GMAIL_DOMAINS.has(domain)) {
    return { deliveryEmail, comparisonEmail };
  }

  let gmailLocal = local.toLowerCase();
  const plus = gmailLocal.indexOf("+");

  if (plus !== -1) {
    gmailLocal = gmailLocal.slice(0, plus);
  }

  // Dot folding is for consumer Gmail. Do not use it for every domain.
  gmailLocal = gmailLocal.replaceAll(".", "");

  return {
    deliveryEmail,
    comparisonEmail,
    abuseIdentityKey: `${gmailLocal}@gmail.com`,
  };
}

Why keep comparisonEmail conservative? SMTP local parts can be case-sensitive, and plus delivery rules are controlled by the receiving domain. A global “remove everything after +” function can merge addresses that belong to different people.

For a domain your company owns, you can use stronger rules because you control its mail server. For a third-party domain, add a rule only after checking provider documentation and adding tests.

The example handles common signup addresses. It is not a complete parser for every allowed RFC 5322 address. If your product supports internationalized addresses, use a maintained parser and test SMTPUTF8 support described in RFC 6531.

Can aggressive email normalization cause account takeover?

It can contribute to an account takeover if a login or recovery flow trusts a false match. Imagine a domain that routes lee@example.com and lee+finance@example.com to different people. If your application strips the tag, both inputs now point to the same account key. A recovery flow that sends a reset link to the newly supplied address could then send access to the wrong person.

That is an example of a vulnerable design, not a claim that all normalization is unsafe. Bind reset tokens to the existing account, send recovery mail to its stored verified address, and require proof before linking accounts. Never use a match on an abuse signal alone to grant access.

Decide what “unique” means first

Before adding a database constraint, ask what you want to protect.

QuestionPossible answerDesign result
Is email used for login?YesOne verified login email may map to one account.
Can users join several workspaces?YesStore membership separately from the user identity.
Can a user add more than one address?YesStore many verified email rows per user.
Are you protecting a free trial?YesLimit the trial benefit, not just account creation.

If a normalized value matches an existing account, do not tell an anonymous visitor that the account exists. Give a neutral message and offer sign-in or password recovery through a rate-limited flow. This helps prevent account enumeration.

Use real anti-abuse controls for real abuse

Email verification proves that someone controls a mailbox. It does not prove they are a new customer or that they should receive another free benefit.

Protect high-value trials, credits, and referrals at the benefit layer. Combine several signals instead of trusting one email rule.

Verify the mailbox

Send a one-time token with a short expiry. Store only a hash of the token, expire it quickly, invalidate it after use, and limit how often a user can request or guess a token.

Use similar messages for existing and new email addresses. Do not help an attacker learn whether an account exists.

Use OpenID Connect for social login

If users sign in with Google, Microsoft, or another provider, use OpenID Connect. It gives your application a provider-specific user identifier.

Store the pair below as the external identity key:

(issuer, subject)

The email claim is useful profile data, but it is not the best permanent identity key. Do not merge accounts only because two providers return the same email string.

Add stronger checks when the benefit is expensive

For an expensive AI trial, cloud credit, or paid promotion, you may need more checks:

  • email and alias signals;
  • workspace invite or company domain information;
  • payment method setup;
  • signup rate by account, IP, network, and device;
  • past trial or coupon use;
  • a risk score and manual review for unusual cases.

Stripe SetupIntents can save and authenticate a payment method without taking a payment immediately. That can raise the cost of repeated abuse, but it also adds signup friction. A card is not proof that there is one person behind an account.

Use phone OTP with care

Phone verification can slow some automated signups. It is not a perfect identity check. Phone numbers can be recycled, shared, virtual, or taken over through SIM swaps.

NIST treats PSTN-based authentication, including SMS, as restricted. Use it when the risk supports the extra friction. Add send and attempt limits, and give legitimate users a recovery option.

Treat device fingerprints as risk signals

Device and browser fingerprints can help find repeated automation. They can also change, be spoofed, and create privacy duties.

OWASP recommends using device data, IP addresses, and remembered-device cookies as risk signals, not as a replacement for authentication. Do not block a user only because one signal looks unusual.

Low risk       -> allow signup
Medium risk    -> require email verification or another check
High risk      -> delay the benefit, ask for stronger proof, or review
Confirmed abuse -> deny and save the reason for the decision

Collect only the data you need. Explain it in your privacy notice, keep it for a limited time, and give users a way to recover from a false positive.

Compare the options

MethodGood forMain problem
Block +Nothing as a general fraud ruleBlocks real users and is easy to bypass.
Exact email uniquenessStopping the same string from registering twiceMisses valid aliases.
Provider-aware normalizationFinding known aliases for selected providersWrong rules can merge separate mailboxes.
Email verificationConfirming mailbox controlDoes not prove one human.
OpenID ConnectLogin and account linkingOne person can still use more than one provider.
Phone OTPA risk-based extra checkNumbers can be shared or attacked.
Payment method checkExpensive trials or creditsAdds cost and signup friction.
Device and rate signalsFinding automation patternsCan produce false positives and privacy concerns.

For most SaaS products, follow these steps:

  1. Accept + and other reasonable email characters in the signup form.
  2. Keep the verified delivery address exactly for sending email.
  3. Lowercase and normalize the domain safely, but do not apply global local-part rules.
  4. Keep provider-specific alias rules separate and versioned.
  5. Define uniqueness for the right thing: an account, a tenant membership, or a trial benefit.
  6. Verify mailbox control before activating the account.
  7. Use OpenID Connect subject IDs for social login.
  8. Add risk checks only when the value at risk needs them.
  9. Log why a signup was challenged or denied, so you can fix false positives.
  10. Give genuine users a recovery path.

The rule to remember is simple: do not judge a person from one character in an email address. Accept valid addresses, keep delivery data intact, and use layered controls to protect valuable benefits.

Common questions about email aliases

Should I strip plus tags before saving an email address?

Keep the delivery address, including its tag. If you need alias detection, calculate a separate value using documented provider rules. Verify and send mail to the submitted address, not a guessed base address.

Does Google Workspace ignore dots like Gmail?

No. Google’s consumer Gmail dot rule does not apply to Google Workspace usernames on custom domains. Keep those dots when comparing addresses. Google’s explanation

Does blocking email aliases prevent free-trial abuse?

No. It removes one route while leaving other mailboxes, domains, and relay services available. Protect trial claims with server-side limits and several risk signals. Phone OTP, OpenID Connect, and payment checks each add evidence, but none guarantees one account per human.

Further reading

David Celis discusses the limits of complex patterns in Stop Validating Email Addresses with Regex. For a production implementation, pair reasonable syntax checks with mailbox verification and server-side abuse controls, using the standards and provider documents below.

References