Cloudflare Workers से email कैसे भेजें

Worker mail server नहीं चला सकता, लेकिन mail server से बात कर सकता है. क्या चलता है, क्या fail होता है और क्यों, यह सब हमने MakiSend बनाते हुए सीखा, जो पूरी तरह Workers पर चलने वाला email API है.

आखिरी update · Markdown संस्करण (अंग्रेज़ी में)

भेजने के तीन तरीके

Worker के पास बाहर जाने के तीन रास्ते हैं. चुनाव इस आधार पर करें कि आप किस पर निर्भर रहना चाहते हैं, code के आधार पर नहीं: code हर हाल में छोटा है.

तरीकाWorker कैसे भेजता हैआपको क्या चाहिएकिस बात का ध्यान रखें
HTTP वाला email APIprovider के API को fetch()उस provider पर account और API keyएक ही provider का payload format, और उसकी प्रति email कीमत
TCP पर SMTPCloudflare के TCP sockets पर nodemailer (nodejs_compat के साथ)किसी भी provider या mailbox के SMTP credentialsअनुमति वाले ports, TLS mode और हर SMTP command पर एक round trip
Cloudflare Email Servicesend_email bindingEmail Sending में जोड़ा गया sender domainकिसी भी recipient को भेजने के लिए Workers Paid चाहिए

SMTP वह तरीका है जो आपको किसी एक provider से बँधने नहीं देता: वही code Google Workspace, Zoho, Microsoft 365, Amazon SES, SendGrid, Mailgun, Postmark, Brevo या Resend के अपने SMTP से भेजता है. इसी में सबसे ज़्यादा फँसने वाली बातें भी हैं.

Worker कौन से SMTP ports इस्तेमाल कर सकता है

Cloudflare port 25 पर outbound connections block करता है, इसलिए Worker सीधे recipient के MX server तक mail नहीं पहुँचा सकता. उसे message किसी submission server को सौंपना होता है, इनमें से किसी एक port पर:

पोर्टसुरक्षामतलब
465इम्प्लिसिट TLS (tls)पहले byte से TLS
587STARTTLS (starttls)Plain text से शुरू होता है, authentication से पहले TLS पर upgrade होता है
2465इम्प्लिसिट TLS (tls)कुछ providers का 465 का विकल्प
2525, 2587STARTTLS (starttls)कुछ providers के 587 के विकल्प
25कोई नहींWorkers पर blocked

Port के साथ सही mode जोड़ें. 465 के साथ STARTTLS, या 587 के साथ implicit TLS, TLS handshake के दौरान fail होता है (wrong version number), कोई भी credential भेजे जाने से पहले. MakiSend configuration save करते समय port 25 को मना कर देता है और port से mode तय करता है.

Workers पर nodemailer चलाना

nodejs_compat compatibility flag चालू होते ही nodemailer Workers पर चलता है: node:net और node:tls Cloudflare के TCP sockets पर map होते हैं. फिर भी तीन चीज़ों ने हमें परेशान किया.

  1. nodemailer hostname resolve करके IP address से connect करता है. workerd port 465 पर implicit TLS के साथ IP address से connection मना कर देता है (proxy request failed, cannot connect to the specified address), जबकि वही server नाम से ठीक खुल जाता है. Error असली वजह के बारे में कुछ नहीं बताता.
  2. Local development में dns.resolve4 और dns.resolve6 implement नहीं हैं, इसलिए default transport wrangler dev में अटक सकता है.
  3. family: 4 के बिना workerd हर send पर network warnings log करता है.

पहली दो समस्याओं का हल nodemailer का getSocket option है: socket खुद hostname से खोलें और nodemailer को दे दें. ज़रूरत होने पर nodemailer तब भी STARTTLS चलाता है और certificate को hostname से check करता है.

smtp-transport.tsts
import * as net from "node:net";
import * as tls from "node:tls";
import nodemailer from "nodemailer";

export function createTransport(env: { SMTP_USER: string; SMTP_PASSWORD: string }) {
  const host = "smtp.example.com";
  const port = 465; // 465 or 2465: implicit TLS. 587, 2525, 2587: STARTTLS.
  const implicitTls = port === 465 || port === 2465;

  return nodemailer.createTransport({
    host,
    port,
    secure: implicitTls,
    requireTLS: !implicitTls,
    family: 4,
    tls: { rejectUnauthorized: true },
    auth: { user: env.SMTP_USER, pass: env.SMTP_PASSWORD },
    // Open the socket by name: workerd refuses implicit TLS to a bare IP address.
    getSocket(_options, callback) {
      const socket = implicitTls
        ? tls.connect({ host, port, servername: host, rejectUnauthorized: true })
        : net.connect({ host, port });
      socket.once(implicitTls ? "secureConnect" : "connect", () =>
        callback(null, implicitTls ? { connection: socket, secured: true } : { connection: socket }),
      );
      socket.once("error", (error) => callback(error, false));
    },
  });
}

MakiSend के version में timeout जुड़ा है, और connection check, test send और असली send तीनों इसी को इस्तेमाल करते हैं: smtp-connection.ts.

Cloudflare Email Service smtp.mx.cloudflare.net पर SMTP भी देता है, लेकिन Worker उस host के लिए socket नहीं खोल सकता. Worker से भेजने के लिए send_email binding इस्तेमाल करें.

Request के अंदर email न भेजें

SMTP हर command के जवाब का इंतज़ार करता है: TCP, TLS, EHLO, AUTH, MAIL FROM, RCPT TO, DATA, QUIT. Workers पर एक send में अच्छी स्थिति में 300 से 400 ms और सबसे खराब स्थिति में लगभग 2 सेकंड लगते हैं, और Worker हर email के लिए नया connection खोलता है. आपके user को इसका इंतज़ार नहीं करना चाहिए, और fail हुआ send खोना नहीं चाहिए.

  • Email save होते ही 202 Accepted लौटाएँ, फिर उसकी id को Cloudflare Queue में डालें.
  • Cloudflare Workflow से भेजें: हर step durable है, इसलिए retry वहीं से शुरू होता है जहाँ run रुका था.
  • सिर्फ़ वही retry करें जो बाद में सफल हो सकता है. MakiSend एक कोशिश और 30 सेकंड के अंतर पर तीन retries करता है; authentication, TLS, DNS, configuration और 5xx errors तुरंत रुक जाते हैं, और जिस email की कोशिशें खत्म हो जाएँ वह dead-letter queue में जाता है.
  • Idempotency-Key header स्वीकार करें, ताकि अपनी request दोबारा भेजने वाला client दो बार email न भेजे.

Network का इंतज़ार CPU limit में नहीं गिना जाता (Workers Free पर हर invocation के लिए 10 ms), इसलिए send खुद free plan में फ़िट हो जाता है. Architecture page एक email को इन steps से गुज़रते हुए दिखाता है.

Fixed IP और inbox

  • Workers साझा Cloudflare addresses से connect करते हैं, जो बदलते रहते हैं. जो provider सिर्फ़ allow-list वाले IPs स्वीकार करता है वह connection मना कर देगा: allow-listing बंद करें और SMTP credentials पर भरोसा करें, या fixed IP वाले server से भेजें.
  • Mail inbox तक पहुँचेगा या नहीं, यह sender domain (SPF, DKIM और DMARC) और provider की reputation पर निर्भर करता है, Worker पर नहीं. हमारे tests में ये तीनों records सेट होने के बाद MakiSend के emails inbox में पहुँचते हैं.
  • Gmail और Google Workspace एक mailbox से रोज़ भेजे जाने वाले emails की सीमा तय करते हैं. ज़्यादा volume के लिए transactional provider इस्तेमाल करें.

या यह काम MakiSend को दें

इस पेज पर जो कुछ भी है, MakiSend में वह सब पहले से तैयार है: authenticated POST /send-email, एक queue, retries वाला durable Workflow, hostname से खुलने वाले sockets पर nodemailer, Cloudflare Email Service binding और logs वाला dashboard. यह open source है और आपके अपने Cloudflare account पर deploy होता है, Workers Free account पर भी.

सवाल

क्या Cloudflare Worker SMTP से email भेज सकता है?

हाँ. nodejs_compat flag के साथ nodemailer Cloudflare के TCP sockets पर चलता है और ports 465, 587, 2465, 2525 या 2587 पर किसी भी SMTP provider से बात करता है. Port 25 blocked है.

Worker से port 25 क्यों काम नहीं करता?

Cloudflare port 25 पर outbound connections block करता है. इसकी जगह अपने provider का submission port इस्तेमाल करें: implicit TLS के साथ 465 या STARTTLS के साथ 587.

Port 465 पर nodemailer "proxy request failed" के साथ fail क्यों होता है?

nodemailer resolve किए गए IP address से connect करता है, और workerd सीधे IP पर implicit TLS मना कर देता है. nodemailer के getSocket option से socket hostname से खोलें.

क्या मैं free plan पर Cloudflare Email Service इस्तेमाल कर सकता हूँ?

किसी भी recipient को भेजने के लिए नहीं: उसके लिए Workers Paid ($5 प्रति माह) चाहिए. Workers Free पर किसी दूसरे SMTP provider से भेजें.