I am responsible for several domain names which are managed through Cloudflare. Unfortunately, they also receive a lot of spam because the email addresses are regularly published openly online and in print.
Since these sites have fairly well known sources of traffic, I’ve written a simple Email Worker script to filter incoming emails. Combined with Cloudflare’s existing spam detection, the filter blocks over 90% of incoming mail, all of which is spam, and let’s through just 3% of emails which should have been blocked.
This script has the additional benefit of enabling multicast addresses, so one email address forwards to multiple destinations (a kind of manual email group or mailing list, if you like).
The script is very simple, so here’s the code for you to re-use (“Unlicense”‘d).
export default {
async email(message, env, ctx) {
// Exact-match strings and regex matches
// - A pretty crude filter but it works well enough for my purposes
const blockList = [
// Example: Block all emails coming from example.com, example.org, etc.
/^.*@example\.[a-zA-Z0-9]+$/,
// The heavy-weight filter - block all emails coming from any top-level
// domain that _isn't_ `.com`, `.org`, `.net` or `.uk`.
/^.*@.*\.(?!(com|org|net|uk))[a-zA-Z]+$/,
// Block a specific domain
/^.*@thisisjunk.com$/,
// Block a specific email address
"frodo@gmail.com"
];
// Check the block list
if (blockList.some((pattern) => typeof pattern == "string" ? pattern == message.from : pattern.test(message.from))) {
// Email blocked
//
// Enabling Cloudflare's worker logs and filtering warning vs info messages
// helps to measure how successful the filter is being.
console.warn(`Rejecting email from "${message.from}" sent to "${message.to}" -- matches block list.`);
message.setReject("Sender address is blocked");
return;
}
// Block emails sent to "honeypot@mydomain.com" - useful to catch some
// persistent spammers who try to hit any email address they can find.
if (message.to == "honeypot@mydomain.com") {
console.warn(`Rejecting email from "${message.from}" sent to "${message.to}" -- honeypot.`);
message.setReject("You've hit the honey pot.");
return;
}
// N.B. Only verified "Destination Addresses" can be used - add them in Cloudflare Email Routing dashboard
const forwardingMap = {
// Forward to one destination
"personA@mydomain.com": "person-A-address@gmail.com",
"personB@mydomain.com": "person-B-address@hotmail.com",
// Forward to multiple destinations
"everyone@mydomain.com": ["person-A-address@gmail.com", "person-B-address@hotmail.com"]
};
// Default forwarding address for emails sent to unrecognised addresses
const catchAllForwardingAddress = "person-C@gmail.com";
// Forwarding address if sending to a Gmail address fails (due to Gmail
// blocking Cloudflare's IP addresses, as happens quite often)
//
// N.B. You don't need this if `catchAllForwardingAddress` doesn't land at
// a Gmail inbox.
const catchAllForwardingAddressNonGmail = "person-C@outlook.com";
// Determine where to attempt to forward the email to
const forwardTo = message.to in forwardingMap
? forwardingMap[message.to]
: catchAllForwardingAddress;
try {
// Normal attempt to forward
if (typeof forwardTo == "string") {
// Single destination
console.info(`Forwarding email from "${message.from}" sent to "${message.to}" on to: ${forwardTo}`);
await message.forward(forwardTo);
}
else {
// Multiple destinations
console.info(`Forwarding email from "${message.from}" sent to "${message.to}" on to: ${forwardTo.join("; ")}`);
// Await all forwards in parallel is more efficient and ensures an error
// at one destination doesn't cause all destinations to fail.
await Promise.all(forwardTo.map(async (address, index) => {
try {
console.info(`[${index+1}/${forwardTo.length}] Forwarding on to: ${address} ...`);
await message.forward(address);
console.info(`[${index+1}/${forwardTo.length}] Forwarding succeeded.`);
}
catch (e) {
console.error(`[${index+1}/${forwardTo.length}] Forwarding failed: ${e}`);
throw e;
}
}));
}
}
catch (e) {
const errorMessage = e.toString();
// Gmail IP ban? Try to forward to the non-Gmail catch-all address
if (errorMessage.includes("Gmail has detected an unusual rate of unsolicited mail")) {
console.warn(`[Error handler] Detected Gmail IP address blocking. Forwarding email from "${message.from}" sent to "${message.to}" on to non-Gmail catch-all: ${catchAllForwardingAddressNonGmail}`);
await message.forward(catchAllForwardingAddressNonGmail);
return;
}
// Otherwise, log the error
console.error(`[Error handler] Failed to forward message: ${e}`);
// Blocked due to Cloudflare's spam filter? Stop here.
if (errorMessage.includes("non-authenticated emails cannot be forwarded")) {
message.setReject("Non-authenticated emails cannot be forwarded");
return;
}
// Try to forward to the catch-all
console.error(`[Error handler] Failed to forward message: ${e}`);
console.info(`[Error handler] Forwarding email from "${message.from}" sent to "${message.to}" on to catch-all: ${catchAllForwardingAddress}`);
await message.forward(catchAllForwardingAddress);
throw e;
}
}
}
Go into Cloudflare
→ Your website/domain
→ Email
→ Email Routing
→
Email Workers
and configure a new worker with the above script (modify as
appropriate for your needs). Then configure Destination addresses
to have
verified destinations (email workers can only forward to verified destinations).
Lastly, configure Routing rules
to direct addresses or the catch-all to your
new email worker. Optionally, enable logging for the worker.