无服务器用Mailgun+Cloudflare搭一个邮件服务(一)

其实现在关于如何避免套了CDN的服务器发送邮件导致IP泄露的方案很多,最直接的方式就是用各种邮件服务商提供的服务来发邮件,邮件接收方查不到你的服务器IP,但邮件服务商可以,所以如果想进一步隐藏IP,可以用Cloudflare Worker来充当一个邮件服务器(实际上算是Serverless),所需成本只有一个域名(Cloudflare上面几美元一个),搭配Cloudflare的各种免费存储和日志功能,还要啥独立邮件服务器

这里的邮件服务以Mailgun为例,首先注册一个Mailgun账号

激活账号后,在左侧工具栏找到Sending,然后New Domain,添加你的域名

接着需要在域名设置下生成一个Sending key,用于调用发送邮件API时的身份验证

完事之后在Domain Settings里面可以看到需要添加的DNS记录

把上面的内容都添加到Cloudflare的域名下的DNS记录

如果你用的是域名下的子域名的话,别忘了为子域名本身加DNS记录

至此其实可以直接调用Mailgun的API发送邮件了,不过这里我们再加一层Cloudflare Worker

新建一个Worker,以Hello World为模板

点击编辑代码,直接在页面内编辑Worker的代码,不需要用wrangler那么麻烦

这里贴出一份Claude写的代码,注意由于Mailgun我选的是欧洲区,所以Mailgun的域名里面带eu:

export default {
  async fetch(request, env, ctx) {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    // CORS
    const corsHeaders = {
      'Access-Control-Allow-Origin': '*', // 可以改成你的实际域名
      'Access-Control-Allow-Methods': 'POST',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    };

    if (request.method === 'OPTIONS') {
      return new Response(null, { headers: corsHeaders });
    }

    try {
      const emailData = await request.json();

      if (!emailData.to || !emailData.subject || !emailData.content) {
        return new Response(JSON.stringify({
          success: false,
          error: 'Missing required fields: to, subject, content'
        }), { 
          status: 400,
          headers: { 'Content-Type': 'application/json', ...corsHeaders }
        });
      }

      const authKey = request.headers.get('Authorization');
      if (!authKey || authKey !== env.AUTH_SECRET) {
        return new Response(JSON.stringify({
          success: false,
          error: 'Missing or invalid Authorization header'
        }), { 
          status: 401,
          headers: { 'Content-Type': 'application/json', ...corsHeaders }
        });
      }

      const mailgunDomain = env.MAILGUN_DOMAIN;
      const mailgunApiKey = env.MAILGUN_API_KEY;
      const mailgunUrl = `https://api.eu.mailgun.net/v3/${mailgunDomain}/messages`;

      const formData = new FormData();
      formData.append('from', emailData.from || `${env.FROM_NAME || 'Store Notification'} <noreply@${mailgunDomain}>`);
      formData.append('to', emailData.to);
      formData.append('subject', emailData.subject);
      
      if (emailData.html) {
        formData.append('html', emailData.html);
      }
      if (emailData.text || emailData.content) {
        formData.append('text', emailData.text || emailData.content);
      }

      if (emailData.cc) formData.append('cc', emailData.cc);
      if (emailData.bcc) formData.append('bcc', emailData.bcc);
      if (emailData.reply_to) formData.append('h:Reply-To', emailData.reply_to);

      formData.append('o:tracking', 'true');
      formData.append('o:tag', emailData.tag || 'store-notification');

      const mailgunResponse = await fetch(mailgunUrl, {
        method: 'POST',
        headers: {
          'Authorization': `Basic ${btoa(`api:${mailgunApiKey}`)}`
        },
        body: formData
      });

      const mailgunResult = await mailgunResponse.json();

      if (mailgunResponse.ok) {
        return new Response(JSON.stringify({
          success: true,
          message: 'Email sent successfully',
          mailgun_id: mailgunResult.id,
          timestamp: new Date().toISOString()
        }), {
          status: 200,
          headers: { 'Content-Type': 'application/json', ...corsHeaders }
        });
      } else {
        console.error('Mailgun error:', mailgunResult);
        return new Response(JSON.stringify({
          success: false,
          error: 'Failed to send email',
          details: mailgunResult.message || 'Unknown Mailgun error'
        }), {
          status: 500,
          headers: { 'Content-Type': 'application/json', ...corsHeaders }
        });
      }

    } catch (error) {
      console.error('Worker error:', error);
      return new Response(JSON.stringify({
        success: false,
        error: 'Internal server error',
        details: error.message
      }), {
        status: 500,
        headers: { 'Content-Type': 'application/json', ...corsHeaders }
      });
    }
  }
};

代码中用了四个环境变量,AUTH_SECRET 相当于调用这个Worker时需要提供的密码,FROM_NAME 和MAILGUN_DOMAIN 使用你的域名,MAILGUN_API_KEY 则是前面创建的Mailgun的Sending key

下一步就是在Worker的设置中添加这四个变量

最后不要忘了添加worker路由,把域名跟worker绑定

用以下命令测试一下worker:

很快就收到了

Worker再搭配Cloudflare的日志和存储功能其实能搭建一个相当好用的邮件服务器,不过这里就不介绍了

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值