Contents
How hard can emails be? Or so I thought. Boy, was I wrong. This post is basically me fucking around with email infrastructure and finding out the hard way.
The Problem
As a person with multiple credit cards (no judging!) â I like to track all of them in a single place using apps such as CRED or Fold. They often ask for email access to read the statements and I donât want to give complete access over my emails.
Hence, I have a separate email where I forward emails via mail rules, but the problem is forwarding them removes the original sender information i.e. the bank here and I want to retain all this information.
In an ideal world, this wouldâve just worked and I will not be writing this post :P
Attempt #1 - Outlook Rule
I created a rule to redirect any mail to my Gmail address for testing purposes. The rule itself works but thereâs no status indication anywhere, so you cannot tell if it failed or not? Bad UX.
Gmailâs postmaster luckily sends a notification that the redirection failed stating: âGmail has detected that this message is likely suspicious due to the very low reputation of the sending domain.â This is a hard SMTP rejection and this is where things get interesting.
The First Clue
The original test email was sent from Gmail to Outlook. When Outlook received it, the authentication results were perfectly fine:
SPF: PASS
DKIM: PASS
DMARC: PASS
Why was Gmail rejecting it when Outlook tried to send it ahead? The important detail is that email forwarding creates another SMTP delivery.
So, the flow essentially is:
The authentication that happened on the first hop doesnât automatically mean the second hop will be accepted.
The Authentication Stuff
So what do SPF, DKIM and DMARC actually have to do with this? These are standard email authentication protocols designed to verify where an email came from and help prevent spoofing and abuse.
Sender Policy Framework (SPF)
SPF is basically:
Is this server allowed to send mail for this domain?
If example.com sends an email through its own mail servers, SPF can verify that those servers are authorized. But forwarding changes the server that delivers the message.
The original message might have come from Googleâs infrastructure:
After Outlook redirects it:
Gmail is now receiving the message from Microsoftâs infrastructure rather than the original senderâs infrastructure. This can make SPF complicated.
DomainKeys Identified Mail (DKIM)
DKIM works differently. The original sender can cryptographically sign the message. That signature can survive forwarding, provided the forwarding system doesnât modify the signed parts of the message.
This is one reason DKIM is particularly important for forwarded mail.
Domain-based Message Authentication, Reporting and Conformance (DMARC)
DMARC ties the authentication results back to the domain in the visible From: address. The policy is published as a DNS TXT record under _dmarc:
_dmarc.example.com TXT "v=DMARC1; p=reject"Here, p=reject tells receiving mail servers to reject messages that fail the domainâs DMARC policy.
Forwarding can therefore create a mismatch between:
- who originally sent the message,
- who is now transmitting it,
- and which domain the message claims to be from.
There are mechanisms such as ARC that can preserve authentication information across forwarding hops, but the receiving mail server still gets to make its own delivery decision.
And Gmail doesnât only look at SPF/DKIM/DMARC. It also has spam and reputation systems to prevent abuse.
The Original Email Was Fine
Looking at the headers of the test message, Outlook had successfully authenticated the original Gmail message:
spf=pass
dkim=pass
dmarc=pass
So the first hop was fine. The failure happened when Outlookâs forwarding engine attempted the second hop. The headers gave me another clue:
Resent-From: <xyz@outlook.in>
auto-submitted: auto-generated
x-ms-exchange-generated-message-source: Mailbox Rules Agent
In other words, this wasnât me manually sending another email. Outlookâs Mailbox Rules Agent was generating the redirected delivery.
Attempt #2 â Add an @outlook.com alias
I found out that this exact issue was supposed to be fixed in 2024?? Microsoft has documented the issue involving country-domain addresses and Gmail rejecting messages with essentially this same error.
The documented workaround was to add an @outlook.com alias. So I added one as abc@outlook.com to the same Microsoft account.
I also switched @outlook.com address as the primary alias instead of @outlook.in. Then I ran the exact same redirect test again. It still failed as the headers for the redirected message still contained:
Resent-From: <xyz@outlook.in>
x-ms-exchange-generated-message-source: Mailbox Rules Agent
The new @outlook.com alias hadnât changed the identity used by the automatic redirect. So, the alias workaround wasnât useful for this particular redirect path.
Attempt #3 â Cloudflare Email Routing
Okay. If Outlook doesnât want to deliver directly to Gmail, maybe I can put another mail routing layer in between. I have routing rules set up such as abc@yashgarg.dev to forward emails to Gmail.
The idea was to let Cloudflare handle the forwarding instead of having Outlook directly deliver the redirected message to Gmail.
Unfortunately, that didnât solve the problem either. Adding another forwarding layer doesnât inherently solve the authentication and reputation problems that can happen when mail crosses multiple SMTP hops.
At this point my email architecture was starting to look like this:
Needless to say, this didnât work either.
Attempt #4 â Email Worker
I was thinking, instead of redirecting directly to my Gmail address, what if I use abc@yashgarg.dev and route it through an Email Worker, then forward the message myself?
Something as simple as:
export default {
async email(message, env, ctx) {
console.log("From:", message.from);
console.log("To:", message.to);
await message.forward("my-mail@gmail.com");
},
};
The idea was that Iâd get the message into my Worker and then have full control over what happened to it. But I didnât realize that Cloudflare performs its authentication checks before the message reaches the Worker. If the inbound email fails those checks, itâs rejected before my code ever gets a chance to run.
Cloudflare performs SPF, DKIM, DMARC, and ARC checks on incoming mail. Messages that fail authentication according to the senderâs DMARC policy are rejected.
So, there goes my last hope.
The Final Solution
I did the thing I really didnât want to do. I moved all my banking-related emails from Outlook to my Gmail address manually.
Not exactly the elegant solution I was looking for, but after all that, it was simply easier than fighting email infrastructure. I spent more time than I wouldâve liked on this but nonetheless, itâs done.
Until next time! ð