Can Email Addresses Contain a Plus Sign? Yes, Preserve It
const accountKey = email.replace(/\+[^@]*(?=@)/, '')
This line looks like a tidy way to stop one person creating several accounts. It is also a way to merge addresses that the receiving domain may treat as different. Yes, an email address can contain a plus sign: RFC 5322 includes + among the characters allowed in an unquoted local part. The syntax does not guarantee that removing +tag produces an equivalent address.
My rule for signup systems is simple: accept and preserve the plus sign, send mail to the address the user supplied, and create an alias-collision key only when you control or explicitly configure that domain's addressing policy.
Why the one-line “fix” merges identities
The regex assumes every receiving system interprets the first plus sign as a separator. Email does not make that promise.
Take the hypothetical address alex+trial@external.example. Its local part is alex+trial. The domain is external.example. Your application does not operate that mail system, so it cannot know whether alex+trial and alex reach the same mailbox.
Stripping the tag before sending a confirmation is worse. You may send the message to a different destination from the one the user entered. Stripping it only for uniqueness checks is safer, but it can still collapse two legitimate identities into one account unless the policy is grounded in that domain's behavior.
This is the same boundary covered in the email case-sensitivity and storage guide: normalize the domain narrowly, but preserve the local part for delivery. Dots, case, and plus tags are not universal rewrite instructions.
Syntax permits +; the receiving domain defines its meaning
RFC 5322 section 3.2.3 defines + as an atext character. That makes an address such as person+notes@example.com syntactically ordinary. A form validator that rejects it merely because of the plus sign is rejecting valid syntax.
RFC 5233 describes subaddressing as adding detail information to a local part, often with a separator such as +. But the same RFC says the encoding is site- or implementation-specific. It even notes that repeated separators can be split according to implementation-defined logic.
person+receipts@example.com
|------| |------| |---------|
possible possible domain
user detail
The words “possible user” and “possible detail” matter. The receiving system assigns those roles. Your signup form only sees a local-part string.
Provider behavior confirms the distinction. As of October 2, 2026, Microsoft documents plus addressing as enabled by default in Exchange Online. Exchange first tries the full recipient and, if that does not resolve, tries the address without the plus tag. An administrator can disable that behavior. This is a provider policy, not a grammar rule that every domain inherits.
A plus sign also does not prove that an address is disposable, abusive, deliverable, or controlled by the person typing it. Syntax, mailbox evidence, and ownership confirmation remain different checks. The validation, verification, and confirmation guide owns those boundaries.
Store delivery and account identity separately
Use separate values for separate jobs:
- Delivery email preserves the local part and is the address used for verification links, notifications, and recovery.
- Account key enforces the comparison policy your product has deliberately chosen.
For an external domain with no configured policy, those values should remain identical. For a domain you operate, you may know that the first plus sign starts a detail tag. The following executed Node.js fixture models that distinction. All domains and addresses are hypothetical.
import { domainToASCII } from 'node:url'
const aliasPolicies = new Map([
['mail.accounts.example', { separator: '+', split: 'first' }],
])
export function prepareAddress(input) {
const value = input.trim()
const at = value.lastIndexOf('@')
if (at <= 0 || at === value.length - 1) {
throw new Error('Malformed email address')
}
const localPart = value.slice(0, at)
const domain = domainToASCII(value.slice(at + 1)).toLowerCase()
if (!domain) throw new Error('Malformed email domain')
const deliveryEmail = `${localPart}@${domain}`
const policy = aliasPolicies.get(domain)
let accountKey = deliveryEmail
if (policy?.separator === '+' && policy.split === 'first') {
const separator = localPart.indexOf('+')
if (separator > 0) {
accountKey = `${localPart.slice(0, separator)}@${domain}`
}
}
return { deliveryEmail, accountKey }
}
This is intentionally not a complete email parser. Run a maintained parser and your ordinary validation before this policy step. The function shows the data boundary: lowercasing the DNS domain does not authorize rewriting the local part, and a plus-tag rule runs only for a configured domain.
OWASP's email canonicalization guidance recommends storing the original input alongside a canonical comparison form and avoiding provider-specific transformations unless you fully control them. Apply the same policy consistently during registration, login, password reset, recovery, and account linking. A different comparison rule in one of those paths creates identity confusion.
The duplicate-trial objection
A fair objection is that preserving plus tags lets one mailbox create several trial accounts. Sometimes it does.
Global tag stripping still does not solve that problem. A user can create another mailbox, use an alias without a plus sign, or choose a different provider. Worse, a false collision can prevent a legitimate person from registering or attach recovery to the wrong account.
Treat an explicitly configured alias match as one risk signal, not proof of abuse. Combine it with the controls that fit your product: confirmed ownership, rate limits, device and payment history, promotion state, or manual review. The free-trial abuse guide covers that wider decision.
A five-step signup flow
- Parse and validate the exact address. Do not reject
+by itself. - Preserve the local part; normalize the DNS domain according to your documented storage policy.
- Verify the exact delivery address and keep uncertain results explicit.
- Create a separate collision key only for a domain policy you control or have deliberately configured.
- Send a single-use confirmation link to the delivery address before treating the account as owned.
The fixture exercised a controlled domain, an unconfigured external domain, a repeated plus sign, and a case-preserved local part. All four checks passed. The important result is not that every plus address collapsed. It is that only the address covered by an explicit policy did.