Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/content/changelog/email-routing/2025-03-12-reply-limits.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: Threaded replies now possible in Email Workers
description: You can now use Email Workers to send multiple replies to the same email thread.
date: 2025-03-12T18:00:00Z
preview_image: ~/assets/images/changelog/ai-gateway/guardrails-social-preview.png
---

Due to increased demand for email use in AI Agents and other forms of task automation, we changed the rules and limits of how Email Workers can [reply](/email-routing/email-workers/reply-email-workers/) to incoming emails.

It's now possible to keep a threaded email conversation with an [Email Worker](/email-routing/email-workers/) script as long as:

* The incoming email has to have valid [DMARC](https://www.cloudflare.com/learning/dns/dns-records/dns-dmarc-record/).
* The email can only be replied to once in the same `EmailMessage` event.
* The recipient in the reply must match the incoming sender.
* The outgoing sender domain must match the same domain that received the email.
* Every time an email passes through Email Routing or another MTA, an entry is added to the `References` list. We stop accepting replies to emails with more than 100 `References` entries to prevent abuse or accidental loops.

Here's an example of a Worker responding to Emails using a Workers AI model:

```ts title="AI model responding to emails"
import PostalMime from "postal-mime";
import {createMimeMessage} from "mimetext"
import { EmailMessage } from "cloudflare:email";

export default {
async email(message, env, ctx) {
const email = await PostalMime.parse(message.raw)
const res = await env.AI.run('@cf/meta/llama-2-7b-chat-fp16', {
messages: [{
role: "user",
content: email.text ?? ''
}]
})

// message-id is generated by mimetext
const response = createMimeMessage()
response.setHeader("In-Reply-To", message.headers.get("Message-ID")!);
response.setSender("[email protected]");
response.setRecipient(message.from);
response.setSubject("Llama response");
response.addMessage({
contentType: 'text/plain',
data: res instanceof ReadableStream ? await new Response(res).text() : res.response!
})

const replyMessage = new EmailMessage("<email>", message.from, response.asRaw());
await message.reply(replyMessage)
}
} satisfies ExportedHandler<Env>;
```

See [Reply to emails from Workers](/email-routing/email-workers/reply-email-workers/) for more information.
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ export default {
}
```

To mitigate security risks and abuse, replying to incoming emails has a few requirements:
To mitigate security risks and abuse, replying to incoming emails has a few requirements and limits:

* The incoming email has to have valid [DMARC](https://www.cloudflare.com/learning/dns/dns-records/dns-dmarc-record/).
* The email can only be replied to once in the same `EmailMessage` event.
* The `In-Reply-To` header of the reply message must be set to the `Message-ID` of the incoming message.
* The recipient in the reply must match the incoming sender.
* The outgoing sender domain must match the same domain that received the email.
* Every time an email passes through Email Routing or another MTA, an entry is added to the `References` list. We stop accepting replies to emails with more than 100 `References` entries to prevent abuse or accidental loops.

If these and other internal conditions are not met, then `reply()` will fail with an exception, otherwise you can freely compose your reply message and send it back to the original sender.
If these and other internal conditions are not met, `reply()` will fail with an exception. Otherwise, you can freely compose your reply message, send it back to the original sender, and receive subsequent replies multiple times.
74 changes: 48 additions & 26 deletions src/content/docs/email-routing/postmaster.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,22 @@ The best way to contact us is using our [community forum](https://community.clou

Through this standard, the sender publishes its public key to a domain's DNS once, and then signs the body of each message before it leaves the server. The recipient server reads the message, gets the domain public key from the domain's DNS, and validates the signature to ensure the message was not altered in transit.

Email Routing signs email on behalf of `email.cloudflare.net`. If the sender did not sign the email, the receiver will likely use Cloudflare's signature for authentication.
Email Routing adds two new signatures to the emails in transit, one on behalf of the Cloudflare domain used for sender rewriting `email.cloudflare.net`, and another one on behalf of the customer's recipient domain.

Below is the DKIM key for `email.cloudflare.net`:

```sh
dig TXT 2022._domainkey.email.cloudflare.net +short
dig TXT cf2024-1._domainkey.email.cloudflare.net +short
```

```sh output
"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiweykoi+o48IOGuP7GR3X0MOExCUDY/BCRHoWBnh3rChl7WhdyCxW3jgq1daEjPPqoi7sJvdg5hEQVsgVRQP4DcnQDVjGMbASQtrY4WmB1VebF+RPJB2ECPsEDTpeiI5ZyUAwJaVX7r6bznU67g7LvFq35yIo4sdlmtZGV+i0H4cpYH9+3JJ78k" "m4KXwaf9xUJCWF6nxeD+qG6Fyruw1Qlbds2r85U9dkNDVAS3gioCvELryh1TxKGiVTkg4wqHTyHfWsp7KD3WQHYJn0RyfJJu6YEmL77zonn7p2SRMvTMP3ZEXibnC9gz3nnhR6wcYL8Q7zXypKTMD58bTixDSJwIDAQAB"
```

You can find the DKIM key for the customer's `example.com` domain by querying the following:

"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnraPy1d8e6+lzeE1HIoUvYWoAOUSREkNHcwxA/ueVM8f6FKXvPu/9gVpgkn8iUyaCfk2z1MW+OVLuFeH64YRMa39mkaQalgke2tZ05SnjRUtYEHYvfrqPuMT+Ouk+GecpgvrtMq5gMXm6ZfeUhQkdWxmMQJGf4fdW5I0piUQJMhK/Qc1dNRSskk" "TiUtXKnsEdjTN2xcnHhyj985S0xOEAxm9Uj1rykPqVvKpqEdjUkujbXOwR0KmHTvPyFpBjCCfxAVqOwwo9zBYuvk/nh0qlDgLIpy0SimrYhNFCq2XBxIj4tdUzIl7qZ5Ck6zLCQ+rjzJ4sm/zA+Ov9kDkbcmyrwIDAQAB"
```sh
dig TXT cf2024-1._domainkey.example.com +short
```

### DMARC enforcing
Expand Down Expand Up @@ -88,14 +93,34 @@ example.com. 300 IN TXT "v=spf1 include:_spf.mx.cloudflare.net ~all"

[The MX (mail exchange) records](https://www.cloudflare.com/learning/dns/dns-records/dns-mx-record/) tell the Internet where the inbound servers receiving email messages for the zone are. In this case, anyone who wants to send an email to `example.com` can use the `amir.mx.cloudflare.net`, `linda.mx.cloudflare.net`, or `isaac.mx.cloudflare.net` SMTP servers.

### Outbound prefixes

Email Routing sends its traffic using both IPv4 and IPv6 prefixes, when supported by the upstream SMTP server.

If you are a postmaster and are having trouble receiving Email Routing's emails, allow the following outbound IP addresses in your server configuration:

**IPv4**

`104.30.0.0/19`

**IPv6**

`2405:8100:c000::/38`

_Ranges last updated: December 13th, 2023_

### Outbound hostnames

In addition to the outbound prefixes, Email Routing will use the domain `email.cloudflare.net` for the `HELO/EHLO` command.
In addition to the outbound prefixes, Email Routing will use the following outbound domains for the `HELO/EHLO` command:

- `cloudflare-email.net`
- `cloudflare-email.org`
- `cloudflare-email.com`

PTR records (reverse DNS) ensure that each hostname has an corresponding IP. For example:

```sh
dig a0-7.email.cloudflare.net +short
dig a-h.cloudflare-email.net +short
```

```sh output
Expand All @@ -107,25 +132,9 @@ dig -x 104.30.0.7 +short
```

```sh output
a0-7.email.cloudflare.net.
a-h.cloudflare-email.net.
```

### Outbound prefixes

Email Routing sends its traffic using both IPv4 and IPv6 prefixes, when supported by the upstream SMTP server.

If you are a postmaster and are having trouble receiving Email Routing's emails, allow the following outbound IP addresses in your server configuration:

**IPv4**

`104.30.0.0/20`

**IPv6**

`2405:8100:c000::/38`

_Ranges last updated: December 13th, 2023_

### Sender rewriting

Email Routing rewrites the SMTP envelope sender (`MAIL FROM`) to the forwarding domain to avoid issues with [SPF](#spf-record). Email Routing uses the [Sender Rewriting Scheme](https://en.wikipedia.org/wiki/Sender_Rewriting_Scheme) to achieve this.
Expand All @@ -136,14 +145,27 @@ This has no effect to the end user's experience, though. The message headers wil

In most cases, Email Routing forwards the upstream SMTP errors back to the sender client in-session.

### Spam and abusive traffic
### Realtime Block Lists

Handling spam and abusive traffic is essential to any email provider. Email Routing filters emails based on advanced anti-spam criteria, [powered by Email Security (formerly Area 1)](/email-security/). When Email Routing detects and blocks a spam email, you will receive a message with details explaining what happened. For example:
Email Routing uses an internal Domain Name System Blocklists (DNSBL) service to check if the sender's IP is present in one or more Realtime Block Lists (RBL) lists. When the system detects an abusive IP, it blocks the email and returns an SMTP error:

```txt
554 <YOUR_IP_ADDRESS> found on one or more DNSBLs (abusixip). Refer to https://developers.cloudflare.com/email-routing/postmaster/#spam-and-abusive-traffic/
554 <YOUR_IP_ADDRESS> found on one or more RBLs (abusixip). Refer to https://developers.cloudflare.com/email-routing/postmaster/#spam-and-abusive-traffic/
```
We update our RBLs regularly. You can use combined block list lookup services like [MxToolbox](https://mxtoolbox.com/blacklists.aspx) to check if your IP matches other RBLs. IP reputation blocks are usually temporary, but if you feel your IP should be removed immediately, please contact the RBL's maintainer mentioned in the SMTP error directly.

### Anti-spam

In addition to DNSBL, Email Routing uses advanced heuristic and statistical analysis of the email's headers and text to calculate a spam score. We inject the score in the custom `X-Cf-Spamh-Score` header:

```
X-Cf-Spamh-Score: 2
```

This header is visible in the forwarded email. The higher the score, 5 being the maximum, the more likely the email is spam. Currently, this system is experimental and passive; we do not act on it and suggest that upstream servers and email clients don't act on it either.

We will update this page with more information as we fine-tune the system.

### SPF record

A SPF DNS record is an anti-spoofing mechanism that is used to specify which IP addresses and domains are allowed to send emails on behalf of your zone.
Expand Down Expand Up @@ -204,4 +226,4 @@ Email Routing does not support sending or replying from your Cloudflare domain.

### Signs such "`+`" and "`.`" are treated as normal characters for custom addresses

Email Routing does not have advanced routing options. Characters such as `+` or `.`, which perform special actions in email providers like Gmail and Outlook, are currently treated as normal characters on custom addresses. More flexible routing options are in our roadmap.
Email Routing does not have advanced routing options. Characters such as `+` or `.`, which perform special actions in email providers like Gmail and Outlook, are currently treated as normal characters on custom addresses. More flexible routing options are in our roadmap.
5 changes: 1 addition & 4 deletions src/content/learning-paths/sase-overview-course.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
"path": "/learning-paths/sase-overview-course/series/evolution-corporate-networks-1/",
"priority": 1,
"description": "Watch this series and learn all about Cloudflare's Secure Access Service Edge (SASE) platform to learn how it can revolutionize your corporate network.",
"products": [
"Cloudflare One"
],
"products": ["Cloudflare One"],
"product_group": "Cloudflare One",
"additional_groups": ["Cloudflare One"]
}

Loading