Email address format is one of the few things every developer believes they already know, and one of the few where the belief is usually narrower than the standard and wider than what services accept. The rules are short: an address splits in two around the at sign, and each half has a length limit expressed in bytes. The complications come from everywhere else — from quoted local parts, from international characters, and from the gap between what a specification permits and what a real service will take.
The two halves around the at sign
An address has a local part on the left of the at sign and a domain on the right. The local part is the receiving system’s own business: inside its domain, it can interpret that label however it likes, which is why two services can treat the same label in completely different ways.
The domain is the part the wider mail system has to be able to find, which is why it is governed by domain rules rather than by mail rules. When a message is sent, the sending side looks up the domain to find the server that accepts mail for it, and the message is then offered to that server. Without a domain record naming a receiving server, there is nowhere for the message to go.
This split is also where the practical advice starts: validate the domain strictly, because getting it wrong means mail that can never be delivered, and be lenient with the local part, because being strict there only rejects addresses that would have worked.
How long can an address be?
The public standard puts limits on both halves and on the total. These are byte counts, not character counts, which matters the moment non-ASCII characters are involved.
| Part | Standard limit |
|---|---|
| Local part | 64 bytes |
| Domain | 255 bytes |
| Whole address including angle brackets | 256 bytes |
A well-known engineering convention is narrower still: many services cap the whole address at around 254 characters, because that figure works out cleanly when the syntax around an address is accounted for. That convention is not the standard, and treating it as the standard is how a database column ends up one character too short for a legitimate address.
The lesson for anyone designing a form or a table is to size fields to the standard and to store the bytes you actually received. Truncating an address silently is worse than refusing it, because the account that is created can never receive anything.
What the standard permits but most services reject
The syntax is far more permissive than the mail most people receive. Among the shapes the standard allows are a local part written inside quotation marks, comments in parentheses, a domain written as a bracketed address rather than a name, and — under the internationalisation extensions — non-ASCII characters in either half.
Very few services accept all of that. Many reject quoted local parts outright, most ignore comments, and support for a bracketed domain is rare. Non-ASCII addresses exist and work in some environments, but almost every consumer service behaves as though the ASCII form is the only one.
The conclusion to carry into your own code is precise: a syntax check is not a statement about the world. Passing it means the address is well formed. It does not mean any service will accept it, and it does not mean the account behind it exists.
Why is a valid address still refused?
Three reasons, none of them about syntax.
The first is that the domain may not accept mail at all. A syntactically perfect address at a domain with no receiving server, or one that refuses all mail, is undeliverable. This is the exact case a disposable inbox is built to sidestep: the temp mail tool hands you an address on a domain that is accepting mail right now, so the delivery path is the part you do not have to arrange.
The second is a per-service rule. A product may restrict which domains it accepts, or which characters it allows in a username, for reasons that have nothing to do with the standard. Those restrictions are policy, and they should be described as policy rather than dressed up as validation.
The third is case and whitespace handling. The domain half is case-insensitive; the local part is technically sensitive, though in practice almost nothing distinguishes case there. Leading or trailing whitespace pasted from a document is a common cause of a refusal that looks like a syntax error, and it is worth trimming before validating rather than after.
Creating addresses that services accept
When you need an address that a random product will accept without argument, the safest shape is the boring one: ordinary letters and digits before the at sign, a conventional domain after it, no quotation marks, no comments, no bracketed domain, no exotic punctuation. That shape passes essentially every validator in use.
The temp mail page produces addresses of exactly that kind, and you can pick a prefix yourself when a form refuses labels that are too short or that look auto-generated. Because the address is created for you rather than registered, the mail that arrives belongs to the errand you started, and the inbox can be abandoned afterwards. If you are choosing between that and a longer-lived arrangement, temp mail versus alias sets out the difference.
Everything discussed here describes the shape of an address, not a person. No address of any kind should be treated as a real identity, and a syntactically correct one tells you nothing about who, if anyone, is behind it.
For developers: validation, field widths and case
Three habits prevent most address-handling defects.
Make validation permissive and layered. Check that there is exactly one at sign outside of quoting, that both halves are non-empty, and that the domain has the structure a domain must have. Resist the temptation to reject anything else, because the exotic shapes you reject may be exactly the addresses you were told to accept. If a service you are integrating with has narrower rules, apply those rules at the integration boundary and say so in the error message.
Size storage to the standard. Give the local part enough room for 64 bytes, the domain enough for 255, and the whole field enough for 256 including punctuation. Test the boundary deliberately with a long local part, because over-long input is the case that quietly truncates.
Normalise deliberately, and document what you normalise. Trimming whitespace and lower-casing the domain are safe and expected. Lower-casing the local part is common but technically a change to the address, so make it a decision you can point at rather than an accident of a helper function.
Then separate the two questions in your tests: is this address well formed, and is it deliverable? A single assert covering both will eventually be wrong about one of them. The verification flow testing article covers what to do once an address passes the first check and mail actually needs to arrive.
Next steps
Take the address column in your product and measure it against the table above; if it is sized on convention rather than on the standard, widen it before somebody’s address is truncated at signup. Then open the temp mail page and generate an address in the boring shape, so that you can see what your validator does with input that is unambiguously acceptable.