The rules
Apply these steps in order:- Remove whitespace from the start and end. Apply Unicode NFKC normalisation. Convert to lowercase.
- If whitespace remains inside the value, it is not an email.
-
Split the value at the last
@into a local part and a domain. If either part is empty, the value is not an email. -
If the domain ends with one
., remove it. If the domain is then empty, the value is not an email. -
If the domain is
googlemail.com, change it togmail.com. -
If the domain is one of the providers below, remove everything from the first
+in the local part:gmail.com,outlook.com,hotmail.com,live.com,msn.com,icloud.com,me.com,mac.com,proton.me,protonmail.com,pm.me,fastmail.com,fastmail.fm -
If the domain is
gmail.com, remove every.from the local part. - If the local part is now empty, the value is not an email.
-
Join the local part,
@and the domain. Hash the result with SHA-256, and encode the digest as lowercase hexadecimal (64 characters).
Why these rules and no others
The rules only join spellings that a mail provider delivers to the same inbox. Gmail ignores dots, and the listed providers delivername+anything@ to name@. Other providers and company domains
can treat + and . as part of the address, so the rules leave them alone. Joining two different
inboxes would make two different people match, and a false match can refuse an innocent person a
service.
Reference implementation
This JavaScript (Node.js 18 or later) implementation matches the specification and passes every test vector on this page.Test vectors
Run your implementation against every row before you send a request. The SHA-256 column is the exact value to send.
In the input column,
␠ marks a space character.
Common mistakes
Hashing the raw email
Hashing the raw email
Hash the normalised value.
Jane.Doe@Example.com and jane.doe@example.com must produce
the same digest.Uppercase or base64 output
Uppercase or base64 output
Send lowercase hexadecimal. The API rejects any other format with
400 INVALID_REQUEST.Hashing twice
Hashing twice
Hash the normalised email once. Do not hash the hexadecimal digest again before you send it.
Applying Gmail rules to every domain
Applying Gmail rules to every domain
Remove dots only for
gmail.com, and remove +tags only for the listed providers.
first.last+news@company.com stays exactly as it is.Splitting at the first @
Splitting at the first @
Split at the last
@. A quoted local part can contain @.Different code for checks and reports
Different code for checks and reports
Use one function for both. If your check and your report hash differently, your own reports
never match your own checks, and nobody else’s do either.
Versioning
The rules on this page are version1. Every check response includes normalisation_version, so you
can confirm which rules the registry expects. If the rules change, the version number changes, and
members receive notice before the change.