---
title: Sending Emails with Cloudflare Workers
author: Max Gialanella
tags: []
categories: []
date: 2023-10-01 20:12:00
---

Cloudflare has a partnership with MailChannels which has allowed Cloudflare to introduce email sending into its web workers. There is some documentation to set this up but its all over the place. Here are the steps I took to get email sending running, using workers.

Before getting started you want to generate public and private keys for your DKIM records.
I grabbed steps from this guide -> https://github.com/cloudsecurityalliance/webfinger.io/blob/main/docs.webfinger.io/DKIM-setup.md

###### Generate a DKIM private and public key:

Private key as PEM file and base64 encoded txt file:

```
openssl genrsa 2048 | tee priv_key.pem | openssl rsa -outform der | openssl base64 -A > priv_key.txt
```

Public key as DNS record:

```
echo -n "v=DKIM1;p=" > pub_key_record.txt && \
openssl rsa -in priv_key.pem -pubout -outform der | openssl base64 -A >> pub_key_record.txt
```

###### Setup DNS records:

SPF record:  
`TXT`, `YOUR DOMAIN.COM`, `v=spf1 ~all`

DMARC record:  
`TYT`,
`_dmark`,
`v=DMARC1; p=none;rua=mailto:741da103196f4adba4f119840d7b8919@dmarc-reports.cloudflare.net`

Mail Channels Record:

> Important note, this is the domain of your worer sending emails. Its probably something like "dev.yourdomain.com" check your worker setup.
> `TXT`,`_mailchannels`,`v=mc1 cfid=YOUR DOMAIN.COM`

Mailchannels Domain Key Record:
`TXT`,`mailchannels._domainkey`,`CONTENTS OF pub_key_record.txt`

###### Setup a new worker for sending emails

Go into cloudflare and setup a new worker.js and paste in the following code.

```
addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {

        let respContent = "";

        // only send the mail on "POST", to avoid spiders, etc.
    if( request.method == "POST" ) {

        const formData = await request.formData();
        const body = {};
        for (const entry of formData.entries()) {
                      body[entry[0]] = entry[1];
              }
        fls = JSON.parse(JSON.stringify(body));

        let send_request = new Request("https://api.mailchannels.net/tx/v1/send", {
        "method": "POST",
        "headers": {
            "content-type": "application/json",
        },
        "body": JSON.stringify({
            "personalizations": [
                { "to": [ {"email": "RECIPIENT@EMAIL.COM",
                        "name": 'Email Tester'}],
                        "dkim_domain": 'YOURDOMAIN.com',
                        "dkim_selector": 'mailchannels',
                        "dkim_private_key": `<${PrivateKey}>`,

                }
            ],
            "from": {
                "email": "admin@ravenwulfconsulting.com",
                "name": "admin",
            },
            "reply_to": {
                "email": `${body.email}`,
                "name": `${body.name || 'No Name Provided'}`,
            },
            "subject": `${body.subject || 'No Subject'}`,
            "content": [{
                "type": "text/plain",
                "value":
`Email: ${body.email}
Name: ${body.name || 'No Name Provided'}

${body.message || 'No Message'}
`,
            }],
        }),
    });

        const resp = await fetch(send_request);
        respContent = resp.status + " " + resp.statusText
    }

    let htmlContent = "<html><head></head><body><pre>" +
        `
            <form method="post">
            Sub: <input type="text" name="subject" value="Testing email sending" /><br>
            Name: <input type="text" name="name" value="Testman Testerguy" /><br>
            Email: <input type="text" name="email" value="testformsubmit@gmail.com" /><br>
            Msg: <input type="text" name="message" value="This is a test email to test the email send worker" /><br>
            <input type="submit" value="Send"/>
            </form>
        ` +
        "<pre>" + respContent + "</pre>" +
        "</body></html>";
    return new Response(htmlContent, {
        headers: { "content-type": "text/html" },
    })
}
```

This code is crap and extremely insecure. Its a combination of code from various sources. Its super crappy. Its just a simple foundation to get your own worker set up.

Pay close attention to the status messages after posting the form.

-   200 - Success
-   400 - DNS records are not set up properly, or didn't propagate. Mailchannels is rejecting request.
-   500 - Post data isn't correct. You won't get any error messages, just a 500

Best of luck getting everything running.

Sources:

-   Seting up DKIM Records: https://github.com/cloudsecurityalliance/webfinger.io/blob/main/docs.webfinger.io/DKIM-setup.md
-   Cloudflare DKIM Explnained: https://www.cloudflare.com/en-ca/learning/dns/dns-records/dns-dkim-record/
-   Mailchannels Adding DKIM to workers: https://support.mailchannels.com/hc/en-us/articles/7122849237389-Adding-a-DKIM-Signature
-   Email Worker Code: https://gist.github.com/IhsanGan/6111b59b9a7b022b5897d28d8454ad8d
-   Email Worker Post: https://support.mailchannels.com/hc/en-us/articles/4565898358413-Sending-Email-from-Cloudflare-Workers-using-MailChannels-Send-API
-   Tons of snippets from this thread: https://community.cloudflare.com/t/send-email-from-workers-using-mailchannels-for-free/361973
