从零到英雄...像专业人士一样发送 AWS SES 电子邮件!

2025-06-10

从零到英雄...像专业人士一样发送 AWS SES 电子邮件!

本文假设读者具有 AWS SES(简单电子邮件服务)的基本知识,例如能够使用 SES 和 Lambda 发送简单的电子邮件或验证身份。

TL;DR

本文分为三个独立的部分:三个问题及其解决方案。如果时间紧迫,请先讨论您认为最重要的部分!

⬇️ 我会定期发布无服务器内容,如果你想了解更多 ⬇️

在 Twitter 上关注我🚀

快速公告:我还在开发一个名为🛡 sls-mentor 🛡的库。它汇集了 30 条无服务器最佳实践,这些实践会在您的 AWS 无服务器项目中自动检查(无论使用哪种框架)。它是免费开源的,欢迎随时查看!

在 Github 上查找 sls-mentor⭐️

✉️ AWS SES ✉️

您可能已经知道,AWS SES 提供了一种使用 AWS 无服务器应用发送电子邮件的绝佳方式。结合 AWS SDK 和 Lambda 函数,它允许您以编程方式向用户发送电子邮件,且只需极少的设置。然而,这种易用性的代价是潜在的错误和陷阱,这些错误和陷阱可能在编码过程的每个步骤中发生。让我引导您解决其中三个问题,并帮助您设计出永远不会被用户误认为垃圾邮件的精美电子邮件。

🚧 🚧 🚧 清洁电子邮件通信道路上的三个障碍

几个月前,我在我正在进行的项目中实施了交易电子邮件发送,并发现了每个开发人员都应该注意的三个主要陷阱(这些陷阱在发送促销电子邮件时也适用!)。

仅使用最少的配置来发送您的电子邮件:

  • 🚧 如果您的发件人信誉下降,您将无法发送消息。
  • 🚧 您的电子邮件可能会被某些用户归为垃圾邮件。
  • 🚧 出于安全原因,您的电子邮件在某些客户端上可能看起来很糟糕(⚠️ gmail 就是其中之一!),或者在某些情况下根本不显示。

本文试图为这三个问题提供简单易行的解决方案。内容丰富,没有特定的顺序,如果其中一个问题比其他问题更重要,请毫不犹豫地先阅读其部分并快速解决!

1. 使用 AWS 维护您的发件人声誉

迈向成功的第一步是确保您的电子邮件能够安全地发送给收件人。这看似微不足道,但 AWS 强制执行严格的规则,这可能会影响您域名的发件人声誉。为了监控发件人声誉,通常使用两个指标:

  • 退回:软退回是由于临时问题(例如收件人邮箱已满)而发生的事件,硬退回是由于永久性问题(例如收件人地址不存在)而发生的事件。

    • 🧐 跳出率超过 5%,您的 SES 帐户将受到 AWS 审核。
    • 退回率超过10% ,您将被禁止发送电子邮件,直至调查完毕。
  • 投诉:当您的收件人手动将您的电子邮件报告为不受欢迎的邮件(基本上是垃圾邮件)时发生的事件。

    • 🧐 投诉率超过 0.1%,您的 SES 帐户将受到 AWS 审核。
    • 投诉率超过0.5% ,您将被禁止发送电子邮件,直至调查完毕。

显然,您不希望自己的账户被列入黑名单,但我还是建议您在账户被审核之前采取行动。为了避免这些不好的事情发生在您身上,以下是我的两个个人建议:

设置警报,以便在为时已晚之前收到警告

您可以设置 Cloudwatch 警报来监控退回率和投诉率,并在达到危险水平时向您发送通知/采取行动。这些 Cloudwatch 警报可以在账户级别配置集级别设置。这样您就可以监控整个账户的声誉,或监控与特定 SES 身份关联的声誉。

我建议您创建一个与您发送电子邮件的 SES 身份关联的配置集,并对其进行专门监控。这将使您能够更快地识别问题的根源,并在必要时仅关闭部分消息传递基础设施。

以下是用于监控跳出率的警报配置的简单示例。如果过去 30 分钟内跳出率突破 4%,则会触发警报:

{
  "...": "...",
  "MetricName": "Reputation.BounceRate",
  "Namespace": "AWS/SES",
  "Statistic": "Average",
  "Dimensions": [
    {
      "Name": "ses:configuration-set",
      "Value": "<your_configuration_set_name>"
    }
  ],
  "Period": 300,
  "EvaluationPeriods": 6,
  "DatapointsToAlarm": 1,
  "Threshold": 0.04,
  "ComparisonOperator": "GreaterThanThreshold",
  "TreatMissingData": "notBreaching",
  "...": "..."
}
Enter fullscreen mode Exit fullscreen mode

下一步是将 SNS(简单通知服务)主题插入到两个信誉指标警报中,然后:

  • 将您的开发人员电子邮件地址订阅到该主题,以便在出现问题时收到警告。
  • 订阅配置集中自动关闭电子邮件发送的 lambda。

警报模式

使用 Typescript 和 AWS SDK v3 禁用配置集:

const client = new SESClient({});
await client.send(
  new UpdateConfigurationSetSendingEnabledCommand({
    Enabled: false,
    ConfigurationSetName: '<your_configuration_set_name>',
  }),
);
Enter fullscreen mode Exit fullscreen mode

避免向有问题的收件人发送电子邮件

有了这些警报,您已经可以快速应对声誉问题。但您还可以采取更多措施,避免向损害您声誉的地址发送多封电子邮件。

实现此目的的一种方法是将退回或投诉的地址添加到抑制列表中。抑制列表有三种类型:

  • 全球层面(AWS 整体)
  • 帐户级别
  • 配置集级别

就像您的警报一样,我建议您尽可能具体,并设置配置集级别抑制列表

启用退回邮件和投诉抑制列表的配置集配置示例。此配置将自动将退回邮件和投诉的电子邮件地址从您的配置集中列入黑名单。您稍后可以手动将其从列表中移除。

{
  "...": "...",
  "SuppressionOptions": {
    "SuppressedReasons": ["BOUNCE", "COMPLAINT"]
  },
  "...": "..."
}
Enter fullscreen mode Exit fullscreen mode

太棒了!现在你确定你的邮件已经发出了🙃。但是你的用户会在收件箱里收到它们吗?如果你不稍微调整一下配置,可能并非所有用户都能收到。

2. 确保你的邮件不会被用户误认为是垃圾邮件

当用户收到电子邮件时,有许多因素决定该电子邮件最终是进入邮箱还是进入垃圾邮件。

在我开发电子邮件发送服务的过程中,我发现了mail-tester:一个评估电子邮件最终进入收件箱概率的绝佳工具。只需向他们提供的地址发送一封测试邮件,它就会分析你的邮件被接收的可能性。

邮件测试器坏了

第一个标记很糟糕,但您可以使用网站提供的提示来修复它!

许多因素决定您的电子邮件是否被视为可疑,让我介绍一下最重要的因素。

电子邮件内容

这是最容易修复的(你的应用上可能已经解决了),但也是最重要的。如果你不遵守规定,邮件客户端通常会自动将你的邮件归类为垃圾邮件。

  • 您的电子邮件绝对应该有一个主题和正文
  • 您的电子邮件不应太短,尤其是包含图像时。
  • 如果您的电子邮件包含链接,请优先使用完整链接而不是缩小的链接
  • 另外,请检查电子邮件中包含的每个链接是否健康并重定向到正常运行的网站
  • 最后,List-Unsubscribe邮件标题可以提升你的声誉,尤其是在你计划发送促销邮件时。你将在本文的第三部分了解如何实现它。

发件人域

邮件客户端检查的第二件事是电子邮件的来源域名。客户端主要检查三项内容,以确保发件人的安全:

  • SPF 和 IP 发送池。默认情况下,SES 使用公共 IP 池,但该 IP 池的信誉随着时间的推移而变得相当糟糕。良好的做法是拥有自己的域名,以便能够将 SPF 配置为引用 SES 上配置的私有 IP 池。
  • DKIM - 电子邮件签名。提供 MAIL FROM 标头真实发送者身份的证明。
  • DMARC - 指定如果 MAIL FROM 和发件人不同时如何操作,以避免其他人对您的域采取行动

在本文的这一部分,我将指导您使用自己的域名向用户发送电子邮件,并向您展示如何在电子邮件上设置 DKIM 和 DMARC。

如果您还没有域名,可以在AWS Route 53上以合理的价格轻松购买。使用您自己的域名可以让您微调其 DNS,从而成为值得信赖的发件人。

然后,让我们在 AWS SES 中创建一个新的身份:您想要创建一个域身份,其域名与您刚刚在 AWS Route 53 上保留的域名相对应。我们还分配您的配置集,该配置集链接到之前设置的云监控警报和抑制列表。

SES 域标识

然后,基于域名的身份认证的一个关键功能是能够指定自定义的 MAIL FROM 域名,这将提升邮件客户端的信任度。请选择您域名的子域名,例如notifications.<your_domain>

使用子域名可以为最终读者增加清晰度和信任度,但它也允许您按子域名分离发件人信誉,从而避免影响您的“整个”域名。(例如,如果您搞砸了促销电子邮件,则不会影响您的交易电子邮件)。

您可以在 MX 失败时保留默认行为,如果您的域托管在 Route 53 上,请单击最后一个复选框以让 AWS 自动完成更新域的 DNS 的工作。

来自域的 SES 邮件

最后,为了获得最大程度的信任,请在您的 SES 身份上设置DKIM。这是一种身份验证方法,可向收件人保证您发件人域名的所有权。只需选择简易 DKIM、RSA_2048_BIT 并勾选所有复选框即可设置强身份验证。

SES DKIM

最后,是时候锦上添花了:前往您域名的 DNS(无论是 AWS Route 53 还是您自己的 DNS 提供商),并向_dmarc TXT其中添加一条值为 的记录v=DMARC1; p=none。这条_dmarc记录会向您的收件人表明您已正确设置 DKIM,并且如果身份验证失败,他们可以拒绝您的电子邮件。

邮件测试器很好

10/10,你终于成功了!现在你可以确信你的用户会看到你的邮件了。但这还没完:默认情况下,AWS SES 几乎不会帮你发送美观且响应迅速的邮件。如果你想让你的邮件看起来像专业内容,你就得自己动手了。

邮件测试器 10/10

3. 设计在任何邮件客户端上都看起来不错的响应式电子邮件

模板电子邮件与原始电子邮件

AWS SES 支持两种不同的模式从 Lambda 函数发送电子邮件:原始邮件和模板邮件。每种解决方案都有其优缺点。

  • 模板电子邮件🗒
    • ✅ 提供快速胜利来设计美观的电子邮件。
    • ❌ 技术上受限。例如,自 2023 年 1 月起,您将无法在邮件中添加自定义标题或附件。
    • ❌ 即使在模板中使用简单的 html + css 也可能会导致一些意想不到的视觉效果。
  • 原始电子邮件⚙️
    • ❌ 更难处理(你必须照顾好一切)
    • ✅ 允许高度定制。我认为,如果搭配合适的库,它们会成为更好的解决方案。

这两个选项允许您发送包含 HTML 内容的邮件,以便用户更方便地阅读。模板化邮件直接基于 HTML 模板,而原始邮件可以解析 HTML 模板的内容并将其包含在邮件中(稍后您将了解如何操作)。

模板通常给人一种控制要发送邮件的用户界面的感觉,但兼容性问题非常普遍,因此您应该使用外部工具来生成兼容所有客户端的模板。一图胜千言,所以让我向您展示一个常见的“模板”情况。我设计了一个简单的 HTML + CSS 模板,用于向我的用户发送模板化的凭证邮件。在某些电子邮件客户端(例如“macOS Mail”)上,我的模板显示完美。但在“gmail”上,一切都崩溃了。

MacOS 邮件与 Gmail

为什么会这样?每个电子邮件客户端对 HTML 和 CSS 的兼容性都不同。caniemail 是一个很棒的网站,可以直观地展示这一点它是 can-i-use 的电子邮件版本,提供有关电子邮件所有兼容性问题的信息。在 caniemail 上,您可以看到flex-direction: columnGmail 桌面版不支持此功能,但 macOS 邮件支持此功能:一切都变得清晰明了!此外,从全球排名来看,macOS 上的所有应用程序通常都运行良好,但在 Windows 和 Web 原生版本上则并非如此。让我们一起寻找解决方案!

电子邮件客户端兼容性

使用 MJML 设计响应式电子邮件模板

每个邮件客户端在显示 HTML、CSS 和图片方面的兼容性各不相同。因此,我建议使用MJML框架,它允许您使用接近 HTML 的标记语言创建/设计邮件模板,并且该模板的编译过程能够解决大多数客户端的兼容性问题。输出模板可用于通过 SES 发送模板化或原始邮件。

以下是我的电子邮件模板的 MJML 代码:

<mjml>
  <mj-head>
    <mj-raw>
      <meta name="color-scheme" content="light" />
      <meta name="supported-color-schemes" content="light" />
    </mj-raw>
    <mj-style>
      a {
      color: #F48668;
      text-decoration: none;
      }
    </mj-style>
  </mj-head>
  <mj-body background-color="#FAFAFA">
    <mj-wrapper border-radius="8px" padding="15px">
      <mj-section background-color="#F48668" border-radius="8px 8px 0 0">
      </mj-section>
      <mj-section background-color="#FFFFFF" border-radius="0 08px 8px">
        <mj-column>
          <mj-text font-family="Trebuchet MS" color="#173940" font-size="22px" font-weight="600" align="center">
            Welcome to my app!
          </mj-text>
          <mj-spacer></mj-spacer>
          <mj-text font-family="Trebuchet MS" color="#173940" font-size="16px" align="center">
            Hello {{username}}, here is your temporary password:
          </mj-text>
          <mj-text font-family="Trebuchet MS" color="#173940" font-size="16px" font-weight="600" align="center">
            {{password}}
          </mj-text>
          <mj-text font-family="Trebuchet MS" color="#173940" font-size="16px" align="center">
            Click the button bellow to log in:
          </mj-text>
          <mj-button background-color="#F48668" border-radius="20px" font-size="16px" font-weight="600" href="http://www.pchol.fr">Join my app</mj-button>
        </mj-column>
      </mj-section>
    </mj-wrapper>
    <mj-wrapper border-radius="8px" padding="15px">
      <mj-section background-color="#FFFFFF" border-radius="8px">
        <mj-column>
          <mj-text font-family="Trebuchet MS" color="#173940" font-size="16px" align="center">
            Ran into a problem ? Do not hesitate to contact me at <a href="mailto:help@pchol.fr"><b>help@pchol.fr</b></a>
          </mj-text>
        </mj-column>
      </mj-section>
    </mj-wrapper>
  </mj-body>
</mjml>
Enter fullscreen mode Exit fullscreen mode
  • <mj-head>部件允许我:
    • 指定我的首选配色方案(仅限浅色)。高级 MJML 技巧包括亮色和暗色显示兼容性。快来看看吧!
    • 编写一些 CSS 代码来进一步完善我的联系邮箱地址。我尽量保持代码简洁,因为它不包含在 MJML 兼容性功能中,并且可能无法在某些客户端上运行。
  • <mj-body>部分描述了将显示的内容。它不是 HTML 格式,但仍然相当容易理解。查看文档以了解有关此语法的更多信息!

通过将 MJML 模板编译为 html + css,您可以在 SES 模板中使用它(或稍后在原始电子邮件中使用它)。要进行编译,请使用手动实时编辑器,或使用 npm:

npm run mjml myTemplate.mjml --output myTemplate.html
Enter fullscreen mode Exit fullscreen mode

来看看结果吧!不同邮件客户端之间没有太大区别。除了联系邮箱地址(我提醒过你🤓)之外,结果完全符合预期。

MacOS Mail 与 Gmail MJML

原始电子邮件的强大功能:附件、图像、自定义标题等

到目前为止所介绍的所有内容都可以使用模板或原始 SES 电子邮件来实现。但从现在开始,您将深入了解原始电子邮件提供的独特可能性,让您的邮件发挥出最大的潜力。

假设我想在邮件标题中添加一个 logo。使用模板邮件,它不可能在每个邮件客户端上都显示。解决方案是发送一封原始邮件,将图片作为附件,并在邮件正文中使用 contentID 引用它。

我修改了我的电子邮件标题 MJML 代码以包含一张图片:

<mj-section background-color="#F48668" border-radius="8px 8px 0 0" padding="0">
  <mj-column>
    <mj-image width="20px" src="cid:my-logo@pchol.fr" alt="My logo"/>
  </mj-column>
</mj-section>
Enter fullscreen mode Exit fullscreen mode

然后,在我用来发送电子邮件的 lambda 中,我可以使用 Nodemailer 库将我的 MJML 模板转换为原始电子邮件,并将附件添加到我的消息中。

import * as aws from '@aws-sdk/client-ses';
import nodemailer from 'nodemailer';
import myHtmlTemplate from './myHtmlTemplate'; // imported as a string

export const handler = async () => {
  const sesClient = new aws.SESClient({});
  const transporter = nodemailer.createTransport({
    SES: { ses: sesClient, aws },
  });

  await transporter.sendEmail({
    from: 'notifications@pchol.fr', // my domain name
    to: '<recipient>',
    subject: 'This a raw email test',
    text: 'This a raw email test',
    html: myHtmlTemplate.replace('{{username}}', 'Pchol').replace('{{password}}', 'very_secret_password'), // manually replace parameters
    attachments: [
      {
        filename: 'pchol-logo.png',
        path: '/opt/pchol-logo.png', // image stored in a Lambda layer
        cid: 'my-logo@pchol.fr', // same as in the template
      },
    ],
  });

  return 'OK!';
};
Enter fullscreen mode Exit fullscreen mode

我邮件中的附件来自 Lambda 层。这些层可以轻松在不同的可用框架上进行设置。

list使用诸如或 之类的附加参数headers,您可以为电子邮件指定自定义标头(这在模板电子邮件中是无法实现的)。例如,要添加List-unsubscribe标头(邮件测试满分的最后一道障碍),您可以将以下代码添加到处理程序中:

list: {
  unsubscribe: {
    url: 'http://www.pchol.fr',
    comment: 'Unsubscribe from this notifications',
  },
},
Enter fullscreen mode Exit fullscreen mode

这是最终结果,包含徽标作为附件和List-unsubscribe标题。当我从基于自定义域名的 SES 身份发送包含 HTML 样式、附件和自定义标题的完整邮件时,它在 mail-tester 上获得了 100% 的评分。

带有附件的最终电子邮件

邮件测试员也很高兴!

邮件测试员列表-取消订阅

SES 原始邮件允许您发送 MIME 格式的邮件,这些邮件可以包含多个内容类型部分(text/html、text/plain……)。如果您打开收到的邮件的源代码,就可以看到这些不同的部分及其内容。您还可以查看刚刚添加的 List-unsubscribe 标头!

...

List-Unsubscribe: <http://www.pchol.fr> (Unsubscribe)
Date: Wed, 4 Jan 2023 17:46:43 +0000
MIME-Version: 1.0

...

----_NmP-92cb611d778ad2fe-Part_1
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit

This a raw email test

...

----_NmP-92cb611d778ad2fe-Part_3
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

... my html ...
Enter fullscreen mode Exit fullscreen mode

这很重要,因为如果客户端拒绝显示您的 html 内容,它将回退到 text/plain 内容。

结论

以下是我在本文中涵盖的所有内容的小清单,您在开发在 AWS SES 上发送电子邮件时应该检查这些内容:

  • ✅ 警报监控我的 AWS 发件人信誉
  • ✅ 配置集级别抑制列表
  • ✅ 自定义域名 SES 身份
  • ✅ 我的域名上的 DKIM
  • ✅ 我的域名上的 DMARC
  • ✅ 模板化邮件与原始邮件的争论
  • ✅ 使用 MJML 的响应式电子邮件模板
  • ✅ 原始电子邮件允许发送 MIME 电子邮件(附件、自定义标题......)

对于每一个问题,我都试图向您展示简单而有效、易于快速实施的对策。

鏂囩珷鏉ユ簮锛�https://dev.to/slsbytheodo/from-zero-to-hero-send-aws-ses-emails-like-a-pro-4nei
PREV
AWS 无服务器入门 - 身份验证
NEXT
关于“初级全栈开发人员”的问题