ReturnPath を専用アドレスに設定するだけで、不要なNDR転送を止められるメール送信には複数のサーバーが連携しています。
あなたのアプリ
│
│ boto3 / SMTP
▼
Amazon SES
│
│ SMTP
▼
受信側MTAサーバー(Gmailなど)
│
▼
受信者のメールボックス
| 用語 | 正式名称 | 役割 |
|---|---|---|
| MTA | Mail Transfer Agent | メールを転送するサーバー |
| MXレコード | Mail Exchanger Record | 「このドメインのメールはこのサーバーへ」を示すDNS設定 |
| SMTP | Simple Mail Transfer Protocol | メール転送のプロトコル |
| From | — | 受信者に表示される送信者アドレス(人間向け) |
| Return-Path | Envelope From | バウンス通知の返送先(受信者には表示されない・サーバー向け) |
| NDR | Non-Delivery Report | メールが届かなかったときに返送される配達失敗通知 |
封筒で例えると:
From = 手紙の中に書く「差出人」(受信者が見る)
Return-Path = 封筒の裏に書く「返送先住所」(郵便局=MTAだけが使う)
💡 ポイント: FromとReturn-Pathは別物。Return-Pathを指定しないと、SESはFromアドレスをReturn-Pathとして扱い、NDRが意図しない宛先に届く。
バウンス(Bounce) = メールが受信側に届かず、送信側に返ってくること。
送信したメール
│
▼
受信側MTAが判定
│
├─ 永続的に届かない ──→ ハードバウンス(Permanent)
│
└─ 一時的に届かない ──→ ソフトバウンス(Transient)
| 項目 | ハードバウンス | ソフトバウンス |
|---|---|---|
| 原因 | 存在しないアドレス / ドメイン | メールボックス満杯 / 受信側サーバー一時障害 |
| 再送すべきか | ❌ 絶対にしない | ⚠️ 一定回数まではOK |
| SESの自動対応 | サプレッションリストに自動登録 | 何もしない(要自前実装) |
| 深刻度 | 高 | 中 |
💡 ポイント: ソフトバウンスはSESが自動対処しないため、放置するとバウンス率が静かに上昇し続ける。
NDR(Non-Delivery Report) = メールが届かなかったときに、送信側へ自動返送される配達失敗通知のこと。基本的に1バウンス = 1NDRですが、中継サーバーが複数ある場合や転送設定によってずれることがあります。
あなたのアプリ
│ From: noreply@example.com
│ Return-Path: (未指定 → Fromと同じになる)
▼
受信側MTA
│
│ 届かなかった場合
▼
From(noreply@example.com)へ NDRを返送
│
▼
意図しない宛先にNDRが届く ⚠️
client.send_email(
Source='noreply@example.com', # Fromアドレス(人間向け)
ReturnPath='bounce@example.com', # バウンス専用の受信口(サーバー向け)
Destination={'ToAddresses': [to]},
Message={...}
)
💡 ポイント:
ReturnPathを専用アドレスに設定するだけで、不要なNDR転送を止められる。最優先で対処すべき設定。
SESはメール送信後の結果を3種類の通知として受け取れます。
| 通知種別 | タイミング | 意味 |
|---|---|---|
| Bounce | 配信失敗時 | バウンスが発生した |
| Complaint | 受信者がスパム報告した時 | 迷惑メール報告された |
| Delivery | 配信成功時 | 正常に届いた |
Amazon SES
│
│ 通知を発火
▼
Amazon SNS(通知の中継役)
│
├──→ Lambda(自動処理)
├──→ SQS(キューに溜める)
└──→ メール(人間が確認)
SNSを挟むことで通知の受け取り方を柔軟に変えられ、将来的に通知先を追加・変更する際もSNS側の設定変更だけで対応できます。
💡 ポイント: 大量バウンスが同時発生する場合はSQSを挟んでまとめて処理する構成が有効。小規模であればSNS → Lambda直結でも十分。
サプレッションリスト = 「このアドレスには送らない」とSESが記録しているブロックリストのこと。
| 種別 | 管理者 | 対象 |
|---|---|---|
| アカウントレベル | 自分で管理 | 自分のAWSアカウント内のみ有効 |
| グローバル | AWS管理 | AWS全体で共有(参照のみ) |
ハードバウンス発生
│
▼
SESが自動でサプレッションリストに追加
│
▼
次回以降、そのアドレスへの送信をSESがブロック
│
▼
バウンスとしてカウントされない ✅
ソフトバウンスは自動登録されないため、自前のDBで管理する必要があります。
💡 ポイント: SESのサプレッションリスト(ハードバウンス)+自前DB(ソフトバウンス)の二段構えが基本構成。
送信レピュテーション = SESにおける「信頼スコア」のようなもの。バウンス率・苦情率をAWSが監視していて、悪化するとアカウントが制限されます。
| 指標 | 内容 | 悪化する原因 |
|---|---|---|
| バウンス率 | 送信数に対するバウンスの割合 | 無効アドレスへの送信・ソフトバウンス放置 |
| 苦情率 | 受信者にスパム報告された割合 | 不要なメールの送信 |
レピュテーション悪化
│
▼
① AWSからの警告メール
│
▼
② 送信一時停止(Probation)
│
▼
③ アカウント停止 ← 最悪のケース
💡 ポイント: 一度停止されると復旧にAWSへの申請が必要で時間がかかる。バウンス率は日頃から監視しておくことが重要。
| 確認場所 | 特徴 |
|---|---|
| SESコンソール(Reputationタブ) | リアルタイムで視覚的に確認できる |
| CloudWatch | 時系列グラフ・アラート設定が可能 |
| SES API | プログラムから取得できる |
CloudWatch
│
├─ メトリクス監視
│ ├─ Reputation.BounceRate
│ └─ Reputation.ComplaintRate
│
▼
アラート発火
│
▼
SNS → メール通知 / Slack通知
💡 ポイント: バウンス率はパーセンテージではなく小数で表示される(例:
0.001=0.1%)。SESコンソールで現在値、CloudWatchで時系列の推移を確認するのがベストプラクティス。
ソフトバウンスをDBで自動管理するための実装パターンです。
SES
│ バウンス発生
▼
SNS トピック
│ 通知を中継
▼
Lambda
│ バウンス種別を判定
├─ ハードバウンス → DBを即座に無効化
└─ ソフトバウンス → DBのバウンス履歴を更新
▼
RDS / PostgreSQL
│ is_activeを管理
▼
次回送信時にフィルタリング
def lambda_handler(event, context):
message = json.loads(event['Records'][0]['Sns']['Message'])
bounce_type = message['bounce']['bounceType']
email = message['bounce']['bouncedRecipients'][0]['emailAddress']
if bounce_type == 'Permanent':
# ハードバウンス → 即無効化
db.execute("UPDATE users SET is_active = FALSE WHERE email = %s", [email])
else:
# ソフトバウンス → バウンス回数を記録
db.execute("UPDATE users SET bounce_count = bounce_count + 1 WHERE email = %s", [email])
送信リスト取得
│
▼
DBで is_active = TRUE のみに絞り込み
│
▼
SESで送信
【必要な設定】
1. VPC設定
└ LambdaをRDSと同じVPCに配置
2. サブネット設定
└ プライベートサブネットに配置
(RDSはインターネットから直接アクセスさせない)
3. セキュリティグループ設定
└ RDSのSG:LambdaのSGからのインバウンドを許可
└ LambdaのSG:RDSへのアウトバウンドを許可
💡 ポイント: LambdaはVPC外のサービス(SNSなど)にアクセスするためにNATゲートウェイまたはVPCエンドポイントも必要になる場合がある。
ユーザー
↓ 問い合わせメール送信
問い合わせ窓口メアド
↓ 社内転送設定
開発者メアド
↓ AWS SESで返信送信
ユーザー
SESで送信したメールの一部が受信側メールサーバーで最終的に配信できず、NDR(配信不能レポート) が問い合わせ窓口メアドに返送されます。その結果、社内の転送設定によって開発者メアドに大量転送されています。
これはSESと受信側サーバーの認識の違いによるものです。
SESが送信
↓
SESは送信完了としてカウント(バウンスとして認識しない)
↓
受信側サーバーが一時的に受信できない
↓
受信側サーバーが数時間〜数日リトライを繰り返す
↓
最終的に諦めてNDRを問い合わせ窓口メアドに返送
↓
社内転送設定により開発者メアドに転送される
| 項目 | 内容 |
|---|---|
| SESのバウンス率が低い | SESは送信完了としてカウントしているため |
| 転送メールが多い | 受信側サーバーが後からリトライ失敗してNDRを返送しているため |
| タイムラグがある | 受信側サーバーのリトライ期間によるもの |
Exchange管理者へ以下を依頼する。
問い合わせ窓口メアドへの転送ルールに、NDR・配信不能メールを転送しない条件を追加してほしい
Exchangeのトランスポートルールで以下の条件のメールを転送対象から除外します。
| 条件 | 値 |
|---|---|
| 件名に含む | Undeliverable、配信不能、Delivery Status Notification |
| 送信者 | MAILER-DAEMON、postmaster@ |
| 項目 | 内容 |
|---|---|
| 原因 | 受信側サーバーのリトライ失敗によるNDRが転送されている |
| SES側対処 | 不可能(SESがバウンスを認識していないため) |
| 解決策 | Exchange管理者によるNDR転送フィルター設定のみ |
| 対応担当 | Exchange管理者 |
| 難易度 | 低 |
💡 ポイント: SESのバウンス率が低くても転送メールが多い場合、原因はSES側ではなく受信側サーバーのリトライ失敗にある。この問題はSES・アプリケーション側では解決できず、Exchangeのフィルター設定が唯一の対処法。
| ミス | 改善策 |
|---|---|
| Return-Pathを未設定のままにする | 専用のバウンス受信アドレスをReturnPathに指定する |
| ハードバウンスだけ対策してソフトバウンスを放置 | SNS + Lambda + DBでソフトバウンスも自動管理する |
| バウンス済みアドレスに再送し続ける | 送信前にis_activeフィルタで除外する |
| バウンス率を定期確認しない | CloudWatchアラートで閾値超過を自動通知する |
| メアドのバリデーションをしない | 登録時にフォーマットチェック+ダブルオプトインを導入する |
| SESのバウンス率が低いから問題なしと判断する | 受信側サーバーのリトライ失敗によるNDRも別途確認する |
- SESのバウンス率は小数表記なのでパーセントに変換して読む必要がある
- NDRとバウンスは基本1対1だが、中継サーバーや転送設定でずれることがある
- SNSを挟む理由は「今」ではなく「将来の拡張性」のため。小規模ならLambda直結でも十分
- ソフトバウンスは自動対策がないため、放置するとじわじわバウンス率を押し上げる
- ReturnPathの設定は最も優先度が高く、難易度も低い。最初に対処すべき
- SESのバウンス率が正常でも転送メール問題が起きる。SES外の問題はExchange側で対処する