23. modulEmail: küldés, fogadás, kézbesíthetőség
Cloudflare for Devs · 23. modul

Email: küldés, fogadás, kézbesíthetőség

Minden SaaS küld emailt (jelszó-visszaállítás, számla, értesítés), és a legtöbb fogad is (support-cím, válaszok). Ez a modul végigveszi a Cloudflare Email Service két felét, a kézbesíthetőség buktatóit, és hogy multitenant rendszerben mire kell figyelni.

23.1A két fél: Sending és Routing

A Cloudflare Email Service két képességet fog össze:

Email SendingEmail Routing
Mit csinálkimenő tranzakciós email küldése (Workerből, REST API-ból vagy SMTP-n)bejövő email fogadása: átirányítás címre, vagy Workerbe
Tipikus használatüdvözlő levél, jelszó-visszaállítás, magic link, számla, riasztássupport@, orders@ címek; email mint API
ElérhetőségWorkers Paid csomagon (bétában indult)ingyenes és fizetős csomagon is
AWS-megfelelőSES (küldés)SES bejövő + Lambda

Küldés Workerből

wrangler.jsonc
{
  "send_email": [
    { "name": "EMAIL", "remote": true }   // remote: valódi API a `wrangler dev` alatt is
  ]
}
server/utils/mail.ts
export async function sendMail(env, opts: {
  to: string; subject: string; html: string; text: string; replyTo?: string;
}) {
  const res = await env.EMAIL.send({
    from: "noreply@myapp.com",
    to: opts.to,
    subject: opts.subject,
    html: opts.html,
    text: opts.text,              // MINDIG legyen sima szöveges változat is
    replyTo: opts.replyTo,
  });
  return res.messageId;           // naplózd — ez a nyomkövetési azonosító
}
Soha ne a kérés-útvonalon küldj emailt. Az emailküldés külső szolgáltatásfüggő és lassú lehet — ha a regisztrációs végpontod bevárja, akkor a felhasználó a levelezőszerver hangulatától függően vár. A helyes minta a 7. és 19. modulból már ismerős: az API-végpont sorba tesz, a queue consumer küld, retry-jal és DLQ-val. Így a „nem ment ki a jelszó-visszaállító email" hibából „a DLQ-ban van, és riasztást kaptunk róla" lesz.
a helyes lánc
// 1) az API-végpont csak sorba tesz
await env.JOBS.send({ type: "mail", template: "password-reset",
                       to: user.email, tenantId, data: { token } });

// 2) a consumer küld — itt van retry, DLQ, és itt mérünk (9. modul)
async queue(batch, env) {
  for (const m of batch.messages) {
    try {
      const { html, text, subject } = renderTemplate(m.body);
      const id = await sendMail(env, { to: m.body.to, subject, html, text });
      env.USAGE.writeDataPoint({ blobs: [m.body.tenantId, "mail_sent", m.body.template],
                                doubles: [1], indexes: [m.body.tenantId] });
      m.ack();
    } catch (e) { m.retry({ delaySeconds: 120 }); }
  }
}

Fogadás: az email mint bemenet

A bejövő oldal az, ami a Workers-modellben tényleg más: a levél egy handlerben landol (ugyanabban a Workerben, ahol a fetch és a queue van — 2. modul):

src/index.ts (vagy a Nitro Worker) — email handler
export default {
  async email(message, env, ctx) {
    const from = message.from;          // feladó
    const to   = message.to;            // pl. support@myapp.com vagy acme@tickets.myapp.com

    // a nyers üzenet feldolgozása (fejlécek, törzs, mellékletek)
    const raw = await new Response(message.raw).text();

    // 1) melyik tenanthoz tartozik? — a címzettből vagy a feladóból
    const tenantId = tenantFromAddress(to);

    // 2) ne itt dolgozzuk fel: sorba tesszük (ugyanaz az elv, mint a webhooknál)
    await env.JOBS.send({ type: "inbound-mail", tenantId, from, to, raw });

    // 3) opcionálisan továbbítás egy valódi postafiókba is
    // await message.forward("support@mycompany.com");
  },
};

Mit lehet ezzel csinálni? Ticket nyitása emailből, válaszok hozzáfűzése a meglévő ticketthez (a In-Reply-To fejléc alapján), mellékletek mentése R2-be (6. modul), automatikus feldolgozás (rendelés-visszaigazolás beolvasása), vagy egyszerűen egy okos átirányítás. A levél elutasítható is (message.setReject()), ha nem felel meg a szabályaidnak.

23.2Kézbesíthetőség — itt dől el, hogy megérkezik-e

Ez a rész független attól, melyik szolgáltatót használod, és a fejlesztők jellemzően alábecsülik. Három DNS-rekord adja az alapot:

RekordMit mondHa hiányzik
SPF (TXT)mely szerverek küldhetnek a domainedrőla leveleid nagy eséllyel spam-be esnek
DKIM (TXT)kriptográfiai aláírás: a levél tényleg tőled jött, és nem módosultugyanaz — sok fogadó elutasít
DMARC (TXT)mit tegyen a fogadó, ha az SPF/DKIM nem stimmel (none / quarantine / reject), és hova küldjön jelentéstnincs visszajelzésed, és a nagy szolgáltatók egyre szigorúbban követelik

Praktikus sorrend a bevezetéshez: állítsd be az SPF-et és a DKIM-et, majd a DMARC-ot először p=none értékkel — így kapsz jelentéseket, de még nem utasít el semmit. Nézd meg pár hétig, mi küld a domainedről (gyakran kiderül, hogy a CRM, a számlázó és a hírlevél-eszköz is), és amikor minden legitim küldő aláírt, lépj quarantine, majd reject felé.

Két gyakorlati szabály, ami sokat számít:Válaszd külön a tranzakciós és a marketing-küldést — lehetőleg külön aldomainre (mail.myapp.com vs. news.myapp.com). Ha a hírlevél panaszokat gyűjt, ne az vigye magával a jelszó-visszaállító leveleidet. ② Mindig küldj sima szöveges változatot is a HTML mellé, kerüld a képekből álló leveleket és a link-rövidítőket — ezek klasszikus spam-jelek.

23.3Multitenant sajátosságok

KérdésMegoldás
Kinek a nevében küldünk?A legegyszerűbb és legbiztonságosabb: mindig a te domainedről (noreply@myapp.com), a tenant nevével a megjelenítendő névben („Acme Kft. — MyApp"), és a tenant címével Reply-To-ban. Így a te reputációdon fut, amit te kontrollálsz.
Az ügyfél saját domainjéről akar küldeniEz a „white-label email": ilyenkor az ügyfélnek kell SPF/DKIM-rekordokat felvennie a saját DNS-ébe, és neked ezt az onboarding-folyamatba kell építened (ellenőrzéssel!). Csak akkor vállald, ha tényleg üzleti igény — a hibás beállítás az ő domainjének reputációját rontja.
Bejövő cím tenantonkéntAldomain-minta: acme@tickets.myapp.com vagy ticket+acme-123@myapp.com. A címből azonosítod a tenantot — de ellenőrizd a feladót is, mert a feladó-cím hamisítható (ezért ne adj jogosultságot pusztán a From alapján!).
Kvóta és visszaélésTenantonkénti napi email-limit (DO-számláló, 7. modul), különben egy elszabadult integráció vagy egy visszaélő ügyfél viszi a reputációdat.
MérésKüldésenként adatpont az Analytics Engine-be (9. modul): tenant, sablon, státusz — ebből lesz a „mennyi emailt küld ez az ügyfél" riport és a számlázási alap.

23.4Sablonok, tesztelés, üzemeltetés

23.5Mikor válassz mégis külső szolgáltatót?

Az Email Service kényelmes (binding, nincs külön kulcs, ugyanaz a számla), de nem mindig a helyes választás. Maradj/válts külső szolgáltatóra (Resend, Postmark, SES…), ha:

A jó hír: bármelyiket választod, a szerkezet ugyanaz marad — API-végpont → Queue → küldő consumer. A szolgáltató cseréje így egyetlen függvény cseréje (sendMail), nem architektúra-váltás.

23.6Ellenőrizd magad

  1. Miért ne küldj emailt közvetlenül a kérés-útvonalról?
    Válasz

    Mert lassú és külső szolgáltatásfüggő: a felhasználó vár, és hiba esetén nincs újrapróbálás. Helyette: az API-végpont sorba tesz, a queue consumer küld — retry-jal, DLQ-val és riasztással, így a hiba látható és javítható marad.

  2. Mit ad a bejövő email() handler, amit a klasszikus szerveren nehéz volt megoldani?
    Válasz

    A levél közvetlenül a kódodban landol, ugyanabban a Workerben, ahol a HTTP- és queue-handlerek — nincs külön IMAP-poller vagy levelezőszerver. Így az email egy bemeneti csatorna lesz: ticket-nyitás, válasz-hozzáfűzés, melléklet R2-be, automatikus feldolgozás.

  3. Mi a három DNS-rekord, ami a kézbesíthetőséget megalapozza, és mi a helyes bevezetési sorrend?
    Válasz

    SPF, DKIM, DMARC. Sorrend: SPF és DKIM beállítása, majd DMARC p=none-nal (jelentések gyűjtése), és csak azután szigorítás quarantinereject felé, amikor minden legitim küldő aláírtan küld.

  4. Multitenant SaaS-ban kinek a nevében küldj emailt?
    Válasz

    Alapesetben a saját domainedről, a tenant nevével a megjelenített névben és a tenant címével Reply-To-ban — így a te (kontrollált) reputációdon fut. Az ügyfél saját domainjéről való küldés (white-label) csak akkor, ha valódi üzleti igény, és akkor is ellenőrzött SPF/DKIM-beállítással az onboardingban.

  5. Mi a veszélye a remote: true email-bindingnak fejlesztés közben?
    Válasz

    Hogy a wrangler dev a valódi szolgáltatást hívja, tehát egy teszt könnyen kiküld éles emailt. Védekezés: nem-produkciós környezetben minden címzettet egy teszt-postafiókba irányíts, vagy csak logolj a tényleges küldés helyett.

Előző22. modul — Biztonság a gyakorlatban: DDoS, WAF, botok, Turnstile Következő 24. modul — E2E-tesztelés, i18n és SEO