According to RFC 2822 Section 3.4.1, the local-part of an email address must follow dot-atom syntax rules:
local-part = dot-atom / quoted-string / obs-local-part dot-atom = 1*atext *( "." 1*atext )
This means consecutive dots (like firstname..lastname
) are not permitted in standard-compliant email addresses. The dots must be separated by at least one atext
character (alphanumeric or certain special characters).
When processing email addresses programmatically, you'll need to validate the local-part format. Here's a Python implementation using regex:
import re def validate_email_local_part(email): local_part = email.split('@')[0] # RFC 2822 compliant regex (simplified) pattern = r'^([A-Za-z0-9!#$%&\'*+/=?^_{|}~-]+(?:\.[A-Za-z0-9!#$%&\'*+/=?^_{|}~-]+)*)$' return bool(re.fullmatch(pattern, local_part)) # Test cases print(validate_email_local_part("valid.email@domain.com")) # True print(validate_email_local_part("invalid..email@domain.com")) # False
Some email systems might accept non-compliant addresses due to:
- Legacy system compatibility
- Overly permissive local policies
- Incomplete RFC implementation
However, for maximum interoperability, your systems should reject addresses with consecutive dots.
If you need to interface with systems that generate non-compliant addresses, consider these approaches:
def sanitize_email(email): local_part, domain = email.split('@') # Replace consecutive dots with single dot sanitized = re.sub(r'\.{2,}', '.', local_part) return f"{sanitized}@{domain}" # Example usage: problematic = "user..name@company.com" print(sanitize_email(problematic)) # "user.name@company.com"
RFC 5322 (which obsoletes RFC 2822) maintains the same restrictions. The updated regex pattern for complete email validation would be:
(?:[a-z0-9!#$%&\'*+/=?^_{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_{|}~-]+)* | "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f] | \$$\x01-\x09\x0b\x0c\x0e-\x7f])*") @ (?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])? | \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3} (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]: (?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f] | \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)$$)
For most practical purposes, the simplified version shown earlier is sufficient.
RFC 2822 clearly defines the syntax for email addresses in section 3.4.1. The local-part of an email address can be either a dot-atom
or a quoted-string
. For most practical purposes, the dot-atom
form is preferred when possible.
The key requirements for a valid dot-atom are:
- Can contain
atext
characters (letters, digits, and certain special characters) - Can contain dots (.) as separators
- Cannot have two consecutive dots
- Cannot start or end with a dot
Here's the relevant ABNF from RFC 2822:
dot-atom = [CFWS] dot-atom-text [CFWS]
dot-atom-text = 1*atext *("." 1*atext)
atext = ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" / "" / "{" / "|" / "}" / "~"
Based on this specification, an email address like firstname..lastname@domain.com
is technically invalid because it contains consecutive dots in the local-part. However, some mail systems might still accept it due to lenient implementations.
Here's how you might implement strict RFC 2822 validation in Python:
import re
def is_valid_email(email):
# RFC 2822 compliant regex (simplified version)
pattern = r'''
^(?!\.) # cannot start with dot
(?:[a-z0-9!#$%&'*+/=?^_{|}~-]+
(?:\.[a-z0-9!#$%&'*+/=?^_{|}~-]+)* # single dots between atext
|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*") # or quoted string
@
(?:(?:[a-z0-9]
(?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]
(?:[a-z0-9-]*[a-z0-9])?|\[(?:
(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}
(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:
(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$
'''
return bool(re.match(pattern, email, re.VERBOSE | re.IGNORECASE))
# Test cases
print(is_valid_email("firstname.lastname@domain.com")) # True
print(is_valid_email("firstname..lastname@domain.com")) # False
print(is_valid_email(".startswithdot@domain.com")) # False
print(is_valid_email("endswithdot.@domain.com")) # False
If you need to handle email addresses with consecutive dots:
- Contact the recipient to verify their correct email address
- If the address is valid but non-compliant, consider using quoted-string form:
"firstname..lastname"@domain.com
- Implement a more lenient validation if your use case requires it
While RFC 2822 prohibits consecutive dots, some major email providers might still accept such addresses. However, relying on this behavior is risky as:
- Intermediate mail servers may reject the message
- The recipient's mail server might have stricter validation
- Future updates to mail systems might enforce the RFC more strictly