Amazon SES でトランザクションメール送信を設計するときの検討項目まとめ

Amazon SES でトランザクションメール送信を設計するときの検討項目まとめ

Amazon SESでトランザクションメール送信を実装するにあたって、検証レコードの設定、サンドボックスの解除、送信履歴の保存、バウンス監視など、意外と多くの検討項目があることに気づきました。本記事では、実装時に検討した項目と対策をまとめています。
2026.10.15

Amazon SES でトランザクションメール送信を設計するときの検討項目まとめ

1. はじめに

製造ビジネステクノロジー部のこがです。
担当プロジェクトでメール送信機能を新たに作成する際に、「Amazon SES」を使えばいいんでしょう程度に考えていたのですが、意外と検討する項目が多かったので整理してみました。
検証レコードや送信履歴の保存など、これから Amazon SES でシステム通知メールを実装するエンジニアの方などの参考になれば幸いです。

2. 前提

  • 送信するのはシステムの処理完了時のトランザクションメール
    • BCC で一度に複数件の宛先に送信する
    • マーケティングメールは本ブログでは取り扱いません
  • メールはそれほど頻繁には送信されない想定(100 通/日程度)
  • 送信先のメールアドレスは Cognito ユーザープールから取得する
  • IaC は Terraform を使用
  • システム向けの受信用メールアドレスなし

3. 今回の構成

最初に今回検討したシステムの簡単な構成図です。

  • SES は SDK を用いて Lambda の中から呼び出し
  • SES の呼び出し前に、Lambda 内で Cognito から送信先メールアドレスを取得
  • Route 53 にメール送信用のドメインと検証レコードを設定
  • メール送信履歴は Kinesis Firehose 経由で S3 に配信
  • CloudWatch Alarm でバウンスのメトリクスを監視し、SNS 経由で開発者にメール通知

4. 検討した事項

4-1. サンドボックスと本稼働アクセス

Amazon SES はデフォルトではサンドボックスとなっており、以下のような制限があります。

  • 認証済みのメールアドレス・ドメイン(およびメールボックスシミュレーター)に対してのみメール送信ができる
  • 24 時間あたりで送信可能なメッセージ数の上限が200通
  • 送信レートは最大 1 通/秒

この制限を解除するにはアカウント/リージョンごとに本稼働アクセス申請が必要です。

PutAccountDetails API や AWS CLI での申請は可能ですが、現状 Terraform からの申請はできません。

今回は手動で AWS コンソールから本稼働アクセス申請を行いました。

参考:

4-2. 検証レコード

SES でメールを送るために DNS に追加するレコードのことです。確かめる側によって役割が 2 つあります。

  1. SES がドメインの所有を確かめる: 「このドメインから送ってよい人か」を判定し、identity を検証済みにします。
  2. メール受信側が、送信元がなりすましでないかを確かめる: Gmail などが受信時に DNS を引き、「本当にそのドメインから来たメールか」を判定します。
役割 1 役割 2
誰が確かめるか SES 受信側(Gmail など)
いつ identity の登録時 メールが届くたび
何を確かめるか 送ろうとしている人が、ドメインの持ち主か 届いたメールが、正規の送信元から来たか
失敗するとどうなるか SES から送信できない 迷惑メール扱いや受信拒否になりやすい

参考:

以下は検証レコードの簡単な説明です。

Easy DKIM

ドメイン所有を証明するレコードです。
Amazon SES の仕様で、3 つの CNAME レコードが登録されます。SES がレコードを検出すると、ID のステータスが検証済みとなります。
自前の鍵を使う場合は、BYODKIM(TXT レコードに公開鍵を登録)を選ぶことも可能です。

また、送信側がメールの内容をもとにメールのヘッダーに付ける電子署名(DKIM 署名)用の公開鍵の置き場所を示す役割もあります。

SPF

メールの受信側が、メールを送ってきたサーバーを送信元ドメインが許可しているかを確認するためのレコードで、実体は TXT レコードです。
SPF 判定で見る送信元ドメインは、SMTP の送信元(MAIL FROM、エンベロープ From)のドメインです。メールソフトに表示される From ヘッダーは確認しません。

SES のデフォルトでは、MAIL FROM が amazonses.com のサブドメインになります。そのため、受信側は amazonses.com の SPF レコードを確認します。このレコードは AWS が登録しているので、利用者が登録する必要はありません。

それでも今回は、ドメインのルートに SPF レコードを登録しました(詳細は後述)。
目的は SES の認証ではなく、ドメイン詐称の抑止です。

ここでいう詐称とは、第三者が SES とは無関係の自前のサーバーから、MAIL FROM に自ドメイン(例: info@example.com)を勝手に名乗ってメールを送ることです。受信側はこのとき example.com の SPF レコードを確認します。

  • SPF レコードが無い場合: 判定は none(情報なし)になり、受信側は許可された送信元かどうかを判断できない。
  • SPF レコードがある場合: 送信元のサーバーが許可リスト(SES)に含まれないため、~all によって softfail となり、「このドメインが許可していない送信元」と受信側に伝えられる。

正規のメールの MAIL FROM は amazonses.com のサブドメインなので、このレコードが参照されるのは自ドメインが MAIL FROM に詐称されたときだけです。
なお、From ヘッダーだけを詐称されるケースは SPF では防げず、後述の DMARC で対処します。

設定する値は AWS がカスタム MAIL FROM 用に案内している v=spf1 include:amazonses.com ~all と同じなので、SES の送信を妨げることはありません。

補足ですが、もし明示的に MAIL FROM を自ドメインにしたい場合は、SES のカスタム MAIL FROM ドメインを設定します。受信側が確認する SPF レコードが自ドメインのものになるので、設定する場合は MAIL FROM 用サブドメインに次の 2 つを登録します。

  • TXT レコード: v=spf1 include:amazonses.com ~all(SES のサーバーからの送信を許可する)
  • MX レコード: 10 feedback-smtp.<region>.amazonses.com(10 は優先度。バウンスや苦情の通知を SES が受け取るため)

DMARC

メールの受信側が、From ヘッダーのドメインが本物かを確認するためのレコードです。本物と確認できなかったメールをどう扱うかも、送信元ドメインがこのレコードで指定します。_dmarc に TXT レコードを登録します(例: v=DMARC1; p=none; rua=mailto:dmarc@example.com)。

受信側は、SPF と DKIM の結果を From のドメインと照らし合わせます。どちらかが pass し、pass したドメインが From のドメインと一致(既定ではサブドメインも可)すれば、DMARC も pass となります。

SPF は MAIL FROM、DKIM は署名のドメインを見るだけで、どちらもメールソフトに表示される From ヘッダーのドメインは検証しません。そのため SPF と DKIM だけでは、From に他人のドメインを書いたなりすましを防げません。
DMARC は From ヘッダーのドメインと SPF または DKIM のドメインとの一致を要求することで、この穴を塞ぎます。

Easy DKIM は自ドメインで署名するので、From ヘッダーのドメインが identity のドメインと一致していれば、Easy DKIM だけでも DMARC は pass します。

なお、DMARC レコードが無いドメインに対して、受信側は DMARC の照合自体をしません(RFC 7489 §6.6.3)。
SPF や DKIM が通っていても DMARC pass にはならず、From を詐称されたときに受信側へ判断材料を渡すこともできません。
p=none でも置いておくことで、正規のメールは DMARC pass として評価され、将来 quarantine や reject へ強化するときは値の変更だけで済みます。

p= で、確認できなかったメールの扱いを指定します。

  • none: 何もしない(受信側に処理を求めない)
  • quarantine: 迷惑メールとして扱う
  • reject: 受信を拒否する

参考:

rua

DMARC 用の TXT レコードに含めるオプションで、rua=mailto:dmarc-reports@example.com のように指定します。

受信側が、自ドメインから送信したメールの DMARC/SPF/DKIM 検証結果を 1 日 1 回程度集計し、XML の Aggregate レポートとして送ってきます。なりすましの兆候や設定不備の発見に使えます。

今回設定した検証レコード

以下を設定しました。

  • Easy DKIM
  • SPF
    • TXTレコード
      • このドメインから送るのは SES だけだと宣言する意図
  • DMARC
    • p=none
      • 受信側は何もしないので抑止力は弱く、将来的には quarantine に上げることを検討

rua は DMARC 検証の合否には関係せず、受信用メールアドレスが用意できなかったため見送りました。

Null MX(MX 0 .)という送信専用ドメインで「受信しない」と宣言する設定も検討したのですが、見送りました。
RFC 7505 は、MAIL FROM や From ヘッダーに使うドメインには Null MX を公開すべきではなく拒否されるおそれがあるとし、拒否時の応答コードも定めています。今回のドメインは From ヘッダーに使うので、迷惑メール判定を減らしたい目的と逆行するためです。

設定方法

以下の流れで設定を検証しました。

  1. Route 53 にメール配信ドメインのホストゾーンを作成
  2. SES identity 作成。設定値は以下
- Identity type: Domain
- Domain: `1.` で設定したドメイン名
- カスタム MAIL FROM 未設定
- DKIM signatures: Easy DKIM
- DKIM signing key length: RSA_2048_BIT
- Publish DNS records to Amazon Route 53: チェック
- DKIM signatures: チェック
  1. 1. のホストゾーンに DKIM CNAME レコードが自動作成される(Terraform でプロビジョニングする場合は明示的に作成する必要あり)。数分経過後、SES domain identity の ID ステータスが検証済みに遷移
  2. 1. のホストゾーンに以下の認証レコードを追加
  • SPF レコード
- Record name: 空
- Record type: TXT
- Value
 - v=spf1 include:amazonses.com ~all
    - v=spf1: バージョン宣言
    - include:amazonses.com: amazonses.com の SPF レコードに書いてある許可リストを、自分の許可リストにも含める
      - SES の送信 IP 範囲は AWS 側で管理されており、その許可リストをそのまま取り込む形
      - Custom MAIL FROM を使用しないので、SES からのメールの SPF 判定にはこのレコードは使われない(受信側はamazonses.com 側を見る)
    - ~all: softfail(疑わしいが受信)  
      - 登録の目的は、第三者が自ドメインをエンベロープ送信者に詐称したときに softfail を返させること
  - TTL:300(デフォルト)
  - Routing policy: Simple routing
  • DMARC レコード
- Record name: _dmarc
- Record type: TXT
- Value: v=DMARC1; p=none;
  - レポートは受信しない
- TTL:300(デフォルト)
- Routing policy: Simple routing

4-3. 共有 IP か専用 IP か

デフォルトでは共有 IP です。
専用 IP は追加料金が必要で、standard と managed があります。

参考:

共有 IP(デフォルト) 専用 IP(standard) 専用 IP(managed)
使う IP 他の SES 利用者と同じ IP プール 自分のアカウントだけが使う IP を借りる 自分のアカウントだけが使う IP を借りる
IP の確保 — 自分で IP を確保し、IP プールを使って管理する SES コンソールでプールを作ると IP が自動で割り当てられる
IP 数 — 自分で判断して増やしたり、減らしたりできる 送信量や ISP(受信側のメールプロバイダー)ごとの事情に合わせて SES が自動で増減する
追加料金 — IP ごとの月額 月額に加えて通数課金
ウォームアップ 不要(すでに温まっている IP が使用される) 時間ベースで、送信量に関係なく 45 日かけて進む ISP ごとに実際の送信状況に合わせて進む
送信レピュテーション(信頼性の評価) 他利用者の影響で IP がブラックリストに入り、メール受信が拒否される可能性がある。AWS が監視して悪質な送信を排除している 自分の送信内容だけで決まる 自分の送信内容だけで決まる
向いているケース 送信量が少ない、または大量送信が定常的ではない場合 大量送信が毎日安定していて、固定 IP が必要な場合 大量送信だが送信パターンが読みにくく、IP 運用の手間を減らしたい場合
その他 — AWS 外の IP の持ち込みも可能 —

専用 IP はウォームアップ期間が必要で、以下のようなイメージで行います。

  • 受信側の ISP は、「その IP から実際にどれだけ、どんな品質のメールが届いたか」で送信レピュテーションを判断する
  • 新しい IP は送信レピュテーションがないので、送信量を少しずつ増やしていく必要がある
  • 送信量が少ないと ISP が判断できないので、少量送信では送信レピュテーションを維持できない

今回は送信量が 100 通/日程度ということもあるので、ウォームアップ済みの共有 IP を選択しました。

4-4. 送信履歴

トレーサビリティの観点から、送信履歴を残したいという要望がありました。

SES には VDM(Virtual Deliverability Manager)ダッシュボードという機能があります。

しかし、メッセージ単位の履歴は最大 30 日しか記録されません。また、BCC で一度に複数の宛先に送信するメールは集計対象外になります。
今回は BCC で複数件に送信する前提なので、VDM ダッシュボードでは要件を満たせませんでした。

Virtual Deliverability Manager only tracks metrics from emails that have one recipient—emails with multiple recipients are not counted in any of the Virtual Deliverability Manager dashboard metrics.

出典:

Emails that you send to multiple recipients.
Note: It's a best practice to send emails to one recipient.

出典:

そこで今回は、SES のイベントを Firehose 経由で S3 に配信し、送信履歴として保存する構成にしました。

この方法ではメール本文は記録されません。
もし本文も残したい場合は、SES を呼び出すメール送信処理の中で別途ログ出力をする必要があります。

SES で記録できるイベントは以下のとおりです。

イベント 発生するタイミング
SEND SES が送信リクエストを受理し、送信処理を開始した
DELIVERY 受信側メールサーバーへの配信に成功した
DELIVERY_DELAY 配信が遅延した。最終的には DELIVERY か BOUNCE に収束する
BOUNCE 配信に失敗した。ハードバウンスは即時に、ソフトバウンスは SES がリトライを諦めた時点で発生する(リトライ中は DELIVERY_DELAY になる)
COMPLAINT 受信者が迷惑メール報告をした
REJECT ウイルス検出などで SES が送信自体を拒否した
RENDERING_FAILURE テンプレートメールの変数展開(レンダリング)に失敗した
OPEN 受信者がメールを開いた(開封トラッキングの設定が必要)
CLICK 受信者がメール内のリンクをクリックした(リンク書き換えの設定が必要)
SUBSCRIPTION SES のリスト管理機能(コンタクトリスト + 購読解除リンク)を使っている場合に、受信者が購読解除などの設定変更をした

参考:

今回はマーケティングメールではないので、OPEN、CLICK、SUBSCRIPTION は対象外にしました。

イベントの記録は、SES の設定セット(Configuration Set)から設定します。
今回の構成ではイベント送信先は Firehose とし、さらに Firehose 側の設定で送信先を S3 にしました。

4-5. バウンスの監視について

SES では、バウンス(BOUNCE)や苦情(COMPLAINT)の発生率が上がると、迷惑メールと判定される可能性が高まります。

バウンスにはハードバウンスとソフトバウンスの 2 種類があります。

  • ハードバウンス: ISP 側での永続的な受信拒否や、配信先メールアドレスが存在しない場合
  • ソフトバウンス: 受信側メールサーバーの一時的な問題(負荷が高い、配信先メールボックスがいっぱいなど)

SES には、アカウントレベルサプレッションリストという機能があります(2019 年 11 月 25 日以降に SES を使い始めたアカウントではデフォルトで有効)。
SES で送信したメールがハードバウンスを起こすと、宛先のメールアドレスがこのリストに登録されます。
以後、同じメールアドレスへの送信は抑止され、バウンス率にも計上されなくなります(ただし、バウンスのメトリクスとイベントは発生)。

なお、アカウントレベルとは別に、全 SES ユーザーに内部的に共有されるグローバルサプレッションリストもあります。

参考:

COMPLAINT(受信者による迷惑メール報告)が発生したメールアドレスもサプレッションリストに登録できます。
しかし、今回はシステム通知であるため、迷惑メール報告は受信者の誤クリックである可能性が高いと考えました。正当な受信者への通知メールが気づかないうちに送られなくなるリスクを避けるため、COMPLAINT は登録対象から除外しました。

サプレッションリストも、送信履歴と同じく SES の設定セットから設定します。

補足ですが、Gmail は SES に苦情データを提供しません。
Gmail 宛てのメールが迷惑メール報告されても COMPLAINT としては通知されず、サプレッションリストにも登録されません。

Gmail doesn't provide complaint data to SES. If a recipient uses the Spam button in the Gmail web client to report a message that they receive from you as spam, they aren't added to your account-level suppression list.

出典:

バウンスの検知方法は次の 2 案を検討しました。

観点 案 A (SNS 直送) 案 B (CloudWatch Alarm)
経路 SES Event Destination → SNS → Email CloudWatch Alarm (Bounce/Complaint count >= 1) → SNS → Email
即時性 秒単位 1〜数分 (Alarm 評価間隔)
全件通知 ○ △ (連続発生時はまとめて 1 通)
個別アドレス追跡 メール本文に含まれる 含まれない(別途送信履歴を確認する必要あり)

案 A はイベントごとに通知が届くため、バウンスが連続すると通知が何度も届きます。また、イベントにはメールアドレスが含まれるため、通知メールにもメールアドレスが載ってしまいます。
これらを考慮し、今回はシンプルな案 B を採用しました。

5. 検証レコードを設定しても迷惑メールと判断されることもある

検証レコードを設定し受信メールの SPF、DKIM、DMARC はすべてパスしていましたが、メール送信機能の開発環境へのデプロイ当初、メールが迷惑メールフォルダに振り分けられてしまうという事象がありました。

メールの仕組み上、新規取得ドメイン/ローンチ直後のレピュテーションが低い状態で、メールが迷惑メールフォルダに入ってしまうことを避けるのは難しいと知りました。

参考:

迷惑メールかどうかを判定するのは、受信側(Gmail、Outlook、Yahoo!、キャリアメールなど)のフィルタです。
一般的にそのフィルタは「送信元がこれまでどう振る舞ってきたか」という実績で信頼度を決めます。新規ドメインやローンチ直後は、この実績がまだありません。送信側でどれだけ正しく設定しても、まだ存在しない実績は作れません。

検証レコードで分かるのは、そのドメインの正当な送信者から送られ、途中で改ざんされていないことのみで、共有 IP のレピュテーションだけでは信用が不十分でした。

実績を積み上げるチートはありませんが、対策としては以下のようなものが挙げられます。

  • カスタム MAIL FROM の設定
  • DMARC を quarantine 以上にしたうえで、メールにブランドロゴを設定する
  • ワンクリック登録解除(List-Unsubscribeヘッダー)の実装
  • 既に信頼のある企業ドメインのサブドメインを利用する
  • ドメインのウォームアップ(段階的な送信量引き上げ)
  • 受信側のシステム(Google Workspace 等)で許可設定(ホワイトリスト)を入れてもらう
  • 「迷惑メールに入る可能性がある」前提での UI/UX 設計(画面上での強いアナウンスなど)
  • 送信先メールアドレス登録のチェック強化
    • 到達確率の高いメールアドレスであることをチェック
    • 有効性確認リンクを送り、届いた確証がある場合だけ登録
  • サービスローンチ前の事前登録者に価値あるメルマガなどを送ってウォームアップ
  • 本番リリース前に、極力多様なドメインのメールアドレスを社員などの関係者から確保する。内部連絡用のメールを関係者宛てにゆっくり送信する。関係者は受け取ったメールに既読をつけるようにする。

今回はトランザクションメールという性質から、最後に挙げた、関係者宛てにゆっくり送信して徐々に信頼を積み上げていく方法をとりました。
また Cognito からの送信先取得時にメールアドレスが検証済みのユーザーのみを送信対象とすることで、到達性の高いメールアドレスであることのチェックとしました。

参考:

6. 最後に

今回メール送信機能を検討してみて、近年は受信側の迷惑メール対策が厳しくなっており、送信ドメインの信頼性を積み上げるためにさまざまな工夫が必要だと知りました。
また、メールが本当に届けたい相手の受信箱に入っているかを送信側から確認するのは難しいため、送信履歴やバウンスなどのログとメトリクスは可能な限り記録しておくべきだと実感しました。
メール配信は奥が深い分野です。もしマーケティングメールの到達率や開封率を上げたい、届くべきメールが届かない、といった込み入った問題が起きた場合は専門家に相談するのがよいと思います。

この記事をシェアする

関連記事