Feature: Email Routing - Multiple Catch-all Addresses

Thank you very much for those examples!

I do find email workers to be a bit odd however. I created a worker which seems to forward properly, however when I look at my dashboard, everything that goes through the worker shows a result of “dropped” in the activity log despite actually being forwarded and getting to the end recipient.

I also discovered that when I send a message from my own domain it doesn’t get through, the worker shows an “error” but I have not found any way to see any detail on what the error is, and the main email forwarding log doesn’t show anything at all.

Right now it’s normal for emails handled by workers to show as dropped in the summary.

As for the error, it’s the same as with normal workers. You could use a third-party library like Sentry to track errors, if you have workers paid you could use logpush, or you can simply tail the worker when it is executing to see the worker. Tailing is the easiest, but you will only see errors that occur during your tail.
In the dashboard you can navigate to your worker, go to “Logs” and then “Begin log stream”, and then send an email, and wait for the event to pop up, and it will tell you why it is erroring.

As for the actual cause, make sure that all of the addresses you are trying to forward to are verified custom addresses in email routing.

So that’s the real rub:

If I send an email to [email protected] from an unrelated email address, everything works fine.
However, if I send an email to that same address, but the from address is within my own domain, or is one of the destination addresses for [email protected] then it doesn’t get delivered.

@Chaika I just tried the log streaming as you suggested.
I pressed the “begin log stream” and then in another window sent a message through. The message never arrived, nothing at all appeared in the real-time logs.

My use case needs me to be able to send to multiple recipients, but not only when none of the recipients are the sender.

One thing that might be worth pointing out is

https://developers.cloudflare.com/email-routing/get-started/test-email-routing/
You should send your test email from a different address than the one you specified as the destination address. For example, if you set up [email protected] as the destination address, do not send your test email from that same Gmail account. Some email providers will discard what they interpret as an incoming duplicate email and will not show it in your inbox, making it seem like Email Routing is not working properly.

@Chaika That doesn’t explain this, because if that were the case, the worker should still log the interaction, and the main dashboard would show the incoming email. Neither of those things is happening.

There is some delay in starting the log stream and getting events. I would start it 10-20s in advance and then try sending it. If it’s not showing in the log stream or in the summary (keeping in mind there’s some delay with those too), then I might assume something is preventing it from reaching email routing at all.

What’s your setup? You’re sending from the same domain you are receiving on, so you have some split setup? Or are you just forwarding emails in workers to custom addresses that get forwarded again?

Setup:

  • Incoming emails go to Cloudflare and are forwarded to various gmail accounts
  • Outgoing emails are sent from gmail, through my own SMTP server allowing me to send emails from my own domain name.

Testing:

  1. started logging on the worker
  2. sent an email from an unrelated external email address
    • result: email arrived properly, email was logged on the worker, and showed on the dashboard
  3. sent an email from one of the underlying gmail addresses
    • result: nothing. No logs, nothing in the dashboard, no evidence anything ever happened other than the email sitting in my sent folder
  4. sent an email from an unrelated email address on my own domain
    • result: nothing. No logs, nothing in the dashboard, no evidence anything ever happened other than the email sitting in my sent folder
  5. sent another email from the unrelated external email address from step 2
    • result: once again everything worked as expected with the log showing it ok, the email getting through, and the dashboard showing the email as “dropped”

I just don’t get it. Anything from my domain, or from a recipient email address just doesn’t get through, and there’s no logs to show anything about it.

hmmm, don’t think I can help you then, sorry. Might have to wait for an email routing team member, perhaps they have more logs on their side. Could try asking in the Cloudflare discord as well.

I tried reproducing it myself, having an email worker that forwards to [[email protected], [email protected]], and sending from [email protected], and it shows fine in [email protected], but gmail discards/doesn’t show it in [email protected] due to discarding duplicates, which is expected.

The only other thing that might be worth noting is that Searching in the Activity Log for the exact sender may show more logs/newer logs that the overall view doesn’t. If it’s not in there at all though, no clue. It should execute the worker eitherway, but maybe not if it’s failing one of the spf/dmarc/dkim checks if you’re sending from your custom domain?

@Chaika
Ok, I looked into the logs on my SMTP server, and it gets weirder. When I send messages from gmail through my SMTP server to Cloudflare, Cloudflare returns a bounced status with unknown user.
But sending to the same email address from an unrelated external email address works properly

It seems that, because all forwarding from Cloudflare (with worker or not) requires the destination email to be verified first, the following wouldn’t work:

So, I have a few questions:

  1. Is it correct that even when using message.forward() from a worker, the destination address has to be verified, i.e. part of “Destination addresses” of /email/routing/routes ?

  2. If so, then what happens concretely when using message.forward()? e.g. would the script error and stop, would the error be visible somewhere, would the subsequent valid message.forward() statements still be executed, etc

  3. If many CF users forward to Gmail addresses, would it make sense for CF to validate the base address (without dots and plus) and consider all valid alternatives as verified? For example, if I validate [email protected], then using a CF worker to forward to [email protected] also succeeds.

  1. Yes, the destination has to be verified and part of destination addresses
  2. The script would error and stop. You can see the error if you tail the worker, or use something like workers paid logpush, or a third-party error monitoring service like Sentry/honeycomb. The email would get bounced and the sending mail servers get an upstream error. For example:
  "exceptions": [    {      "name": "Error",      "message": "destination address not verified",      "timestamp": 1675092801306    }
host route2.mx.cloudflare.net[162.159.205.19]
    said: 521 5.3.0 Upstream error, please check
    https://developers.cloudflare.com/email-routing/postmaster for possible
    reasons why. 
  1. Of course, I am just a community member and I cannot say for sure what the email routing team will do, but I would doubt they would do something like that and try to parse emails to support specific provider’s quirks. I also don’t really see how that would be helpful, as you cannot see (without manually looking at the original message/raw headers), which specific inbox that got delivered to. Since the message was forwarded, gmail just shows the “to” address being your custom domain.
1 Like

Is it possible in 2023? It’s important for me. +10086 to the request here.

+1 on routing to multiple destination addresses for custom addresses

The lack of this feature is preventing me from transferring a couple of domains from Namecheap to Cloudflare.

I’m very motivated, so I was able to get around this by using Email Workers. Basically, I created a simple email worker like this:

export default {
  async email(message, env, ctx) {
    await message.forward("[email protected]");
    await message.forward("[email protected]");
  }
}

And set that Worker as the destination instead of an email address. Note the email addresses need to be verified apriori.