目次
- 自前SMTPから送ったメールが届かない理由
- 受信側は「送信者を信用する根拠」を探している
- SPF・DKIM・DMARCの仕組みとDNSレコード例
- SPF:このドメインのメールを送ってよいサーバーの一覧
- DKIM:メールに電子署名を付ける
- DMARC:認証に失敗したときの扱いを宣言する
- 送信サービス(SES・SendGrid・Resend)でのドメイン認証
- 手順はどのサービスもほぼ同じ
- 通知メールにはサブドメインを使う
- DMARCレポートの読み方とバウンス処理
- レポートで「自分が知らない送信元」を見つける
- バウンスと苦情を処理する
- Gmailの送信者ガイドラインへの対応
- 「一括送信者」の要件は事実上すべての送信者の基準
- トラブル事例:DMARCを設定したら通知メールが届かなくなった
- まとめ
会員登録の確認メールやパスワードリセットのメールが「届かない」「迷惑メールに入る」という問い合わせは、Webアプリの運用で最も多いトラブルのひとつです。開発中は自分のGmailに届いていたのに、本番で利用者が増えた途端に到達率が落ちる、というのも典型的なパターンです。
この記事では、自前のSMTPサーバーから送ったメールが届かない理由を仕組みから説明し、SPF・DKIM・DMARCという3つの送信ドメイン認証の役割とDNSレコードの具体例、Amazon SES・SendGrid・Resendなどの送信サービスでのドメイン認証の手順、DMARCレポートの読み方、バウンス処理、Gmailの送信者ガイドラインへの対応までを解説します。DNSレコードの種類やTTLの基本はドメインとDNS設定の完全ガイドで扱っているので、ここでは「メールを届けるために何をどう設定するか」に集中します。
自前SMTPから送ったメールが届かない理由
受信側は「送信者を信用する根拠」を探している
メールの仕組み(SMTP)はもともと、差出人アドレスを誰でも自由に名乗れる設計です。そのため受信側のメールサービス(Gmail、Microsoft 365、各プロバイダ)は、届いたメールを「本当にそのドメインの正当な送信者から来たか」「そのドメイン・IPアドレスは過去に迷惑メールを送っていないか」という観点で採点し、疑わしければ迷惑メールフォルダに入れるか、受信自体を拒否します。
自前のサーバー(EC2やVPS)でPostfixを立ててアプリから直接送る方式が届きにくいのは、次の理由が重なるからです。
- 送信元IPに評判(レピュテーション)がない:新しいIPからいきなり大量に送ると、それ自体が迷惑メールの特徴と一致する。クラウドの動的IPは過去に他の利用者が悪用していた可能性もある。
- 逆引きDNS(PTRレコード)が整っていない:送信元IPを逆引きしたホスト名と、名乗っているホスト名が一致しないと減点される。
- SPF・DKIM・DMARCが未設定:ドメインの正当性を証明する手段がないため、なりすましと区別がつかない。
- バウンス(配信不能)を放置している:存在しないアドレスへ送り続けると、受信側から「リストの管理をしていない送信者」とみなされる。
したがって実務上の結論は、「送信は評判管理を専門にしている送信サービスに任せ、自社ドメインの認証(SPF・DKIM・DMARC)は自分で正しく設定する」という分担になります。
SPF・DKIM・DMARCの仕組みとDNSレコード例
SPF:このドメインのメールを送ってよいサーバーの一覧
SPF(Sender Policy Framework)は、「example.comを名乗るメールは、これらのIPアドレス/サービスから送られる」という一覧をDNSのTXTレコードで公開する仕組みです。受信側は、エンベロープFrom(Return-Path)のドメインのSPFレコードを引き、実際の送信元IPが一覧に含まれるかを確認します。
; example.com のSPFレコード(TXT)。SES と Google Workspace から送る場合の例
example.com. IN TXT "v=spf1 include:amazonses.com include:_spf.google.com -all"
; -all : 一覧にない送信元は不合格(fail)とする
; ~all : 一覧にない送信元は soft fail(疑わしいが拒否はしない)
; SPFレコードは1ドメインに1つだけ。複数書くと無効になる
; include の連鎖でDNS参照が10回を超えると permerror になる
SPFの弱点は、メールが転送されると送信元IPが転送サーバーのものになり、不合格になることです。また、SPFが検証するのは利用者に見えている差出人(ヘッダーFrom)ではなくReturn-Pathのドメインである点に注意してください。
DKIM:メールに電子署名を付ける
DKIM(DomainKeys Identified Mail)は、送信サーバーがメールのヘッダーと本文に秘密鍵で署名を付け、受信側がDNSに公開された公開鍵で検証する仕組みです。IPアドレスに依存しないため転送に強く、本文の改ざんも検出できます。公開鍵は「セレクタ._domainkey.ドメイン」というTXTレコードに置きます。送信サービスを使う場合、鍵は自動生成され、指定されたCNAMEまたはTXTレコードをDNSに追加するだけで済みます。
; DKIM公開鍵の例(セレクタ "s1")。送信サービスが生成した値をそのまま登録する
s1._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
; SESの場合はCNAMEを3つ登録する形式(Easy DKIM)
abc123._domainkey.example.com. IN CNAME abc123.dkim.amazonses.com.
DMARC:認証に失敗したときの扱いを宣言する
DMARC(Domain-based Message Authentication, Reporting and Conformance)は、SPFとDKIMの結果を「利用者に見えている差出人ドメイン(ヘッダーFrom)」と突き合わせ(アライメント)、不合格だった場合に受信側がどうすべきかをドメイン所有者が宣言する仕組みです。加えて、認証結果の集計レポートを受け取ることができます。
; DMARCレコード(_dmarc.ドメイン のTXT)
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; pct=100; adkim=r; aspf=r"
; p=none : 監視のみ(まずはここから始める)
; p=quarantine : 不合格メールを迷惑メールフォルダへ
; p=reject : 不合格メールを拒否
; rua : 集計レポート(XML)の送り先
; adkim/aspf : アライメントの厳格さ(r=relaxed, s=strict)
| 仕組み | 証明するもの | 検証対象のドメイン | 弱点 |
|---|---|---|---|
| SPF | 送信元IPが許可されている | Return-Path(エンベロープFrom) | 転送で失敗、DNS参照10回制限 |
| DKIM | メールが改ざんされていない・署名者が正当 | 署名ヘッダーのd= | 署名対象外のヘッダー改変は検出不可 |
| DMARC | 上記がヘッダーFromと一致し、失敗時の方針が明示されている | ヘッダーFrom | 単独では何も認証しない(SPF/DKIMが前提) |
3つは競合ではなく重ね合わせです。SPFかDKIMのどちらか一方がヘッダーFromと整合して合格すれば、DMARCは合格になります。実務ではDKIMを主、SPFを従と考え、両方を設定するのが基本です。
送信サービス(SES・SendGrid・Resend)でのドメイン認証
手順はどのサービスもほぼ同じ
- 送信サービスの管理画面で送信ドメイン(例:example.com、または通知専用のmail.example.com)を登録する。
- 表示されたDKIM用のCNAME(またはTXT)レコードと、SPF用のincludeまたはカスタムReturn-Path用のMX/TXTレコードをDNSに追加する。
- 管理画面で「検証済み」になるのを待つ(DNSの反映に時間がかかる場合がある)。
- DMARCレコードを
p=noneで追加し、レポート送付先を設定する。 - テスト送信し、受信側でヘッダーの
Authentication-Resultsを確認する。
# DNSレコードの確認(反映されているか、値が正しいか)
dig +short TXT example.com
dig +short TXT _dmarc.example.com
dig +short CNAME abc123._domainkey.example.com
# 受信したメールのヘッダーで認証結果を確認する(Gmailなら「メッセージのソースを表示」)
Authentication-Results: mx.google.com;
dkim=pass header.i=@example.com header.s=abc123;
spf=pass (google.com: domain of bounce@mail.example.com designates ...) smtp.mailfrom=mail.example.com;
dmarc=pass (p=NONE) header.from=example.com
各サービスの特徴を簡単に整理すると、Amazon SESは料金が安くAWS内で完結し、送信量の上限解除(サンドボックス解除)の申請が必要です。SendGridは管理画面とテンプレート機能が充実し、Resendは開発者向けのシンプルなAPIが特徴です。いずれもAPIキーの管理が必要で、キーの保管はシークレット管理入門の方法に従ってください。
通知メールにはサブドメインを使う
アプリからの自動送信(通知・認証コード)と、社員が使う業務メール(Google Workspaceなど)を同じドメインで送ると、片方の問題がもう片方の評判に波及します。noreply@mail.example.comのようにサブドメインを分け、そのサブドメインだけを送信サービスで認証する構成にすると、影響範囲を切り離せます。
DMARCレポートの読み方とバウンス処理
レポートで「自分が知らない送信元」を見つける
DMARCの集計レポート(rua)は、受信側から1日1回程度、XML形式で送られてきます。内容は「どのIPから、何通、SPF・DKIMの結果はどうだったか」の一覧です。生のXMLは読みにくいので、無料の可視化サービスやレポート解析ツールを経由するのが一般的です。見るべきポイントは次の3つです。
- 正当な送信元が合格しているか:SESやWorkspaceからのメールがpassになっていなければ、設定ミス。
- 知らない送信元がないか:昔契約したメール配信ツール、営業部門が勝手に使っているSaaS、あるいはなりすまし。前者はSPFに追加、後者は
p=rejectへの移行で排除する。 - 転送による失敗の割合:メーリングリスト経由などでSPFが失敗するのは正常。DKIMが通っていれば問題ない。
運用の流れは、p=noneで2〜4週間レポートを観察し、正当な送信元がすべて合格するよう修正してからp=quarantine(必要ならpct=10で段階的に)、最終的にp=rejectへ進めます。いきなりp=rejectにすると、把握していなかった正当なメールが消えます。
バウンスと苦情を処理する
存在しないアドレスへの送信(ハードバウンス)を繰り返すと、送信サービスのアカウントが停止されることがあります。送信サービスはバウンスや「迷惑メール報告」をWebhookで通知してくれるので、アプリ側で受け取り、該当アドレスへの送信を止める処理を必ず実装してください。
# SESのバウンス通知(SNS経由)を受け取り、送信停止リストに入れる例(Python/Flask)
@app.post("/webhooks/ses")
def ses_webhook():
msg = json.loads(request.json["Message"])
if msg.get("notificationType") == "Bounce" and msg["bounce"]["bounceType"] == "Permanent":
for r in msg["bounce"]["bouncedRecipients"]:
suppress_email(r["emailAddress"], reason="hard_bounce")
elif msg.get("notificationType") == "Complaint":
for r in msg["complaint"]["complainedRecipients"]:
suppress_email(r["emailAddress"], reason="complaint")
return "", 200
送信処理自体はリクエストの中で同期的に行わず、キューに入れてバックグラウンドで送り、失敗時はリトライする設計が望ましいです。その設計はバックグラウンドジョブの設計で解説しています。
Gmailの送信者ガイドラインへの対応
「一括送信者」の要件は事実上すべての送信者の基準
Googleは2024年以降、Gmail宛てに1日あたり5,000通以上を送る「一括送信者」に対して、SPFとDKIMの両方の設定、DMARCの設定(最低でもp=none)、ヘッダーFromとのアライメント、ワンクリック配信停止(List-Unsubscribeヘッダー)、迷惑メール報告率を低く保つこと(Postmaster Toolsで0.3%未満が目安)、逆引きDNSの整備、TLSでの送信などを要求しています。5,000通未満でも、SPFまたはDKIMのどちらかと逆引きDNSは必須です。
実務上は、送信量にかかわらず一括送信者の要件を満たしておくのが安全です。特にマーケティング系のメールにはList-UnsubscribeとList-Unsubscribe-Postヘッダーを付け、配信停止の意思表示を確実に処理してください。配信停止や同意の法的な取り扱いはオプトイン/オプトアウト完全ガイドで扱っています。
トラブル事例:DMARCを設定したら通知メールが届かなくなった
症状:セキュリティ強化のためDMARCをp=rejectで設定した翌日から、アプリのパスワードリセットメールが利用者に届かなくなった。
原因:アプリはSendGrid経由でnoreply@example.comを差出人に送っていたが、SendGridのドメイン認証を行っておらず、DKIM署名はSendGridのドメイン、SPFもSendGridのReturn-Pathで合格していた。DMARCのアライメント(ヘッダーFromのexample.comと一致すること)を満たしていなかったため、それまでは「認証は通るが整合しない」状態で届いていたものが、p=rejectにより拒否されるようになった。
対処:即座にp=noneへ戻して配信を復旧。SendGridでexample.comのドメイン認証(DKIMのCNAME登録とカスタムReturn-Path)を実施し、DMARCレポートで全送信元がpassになったことを2週間確認してから、段階的にp=rejectへ戻した。この事例の教訓は「DMARCはSPF/DKIMが通っているかではなく、ヘッダーFromと整合しているかを見る」という点にあります。
まとめ
アプリからのメールを確実に届けるには、送信は評判管理を専門とする送信サービスに任せ、自社ドメイン側でSPF(許可する送信元)・DKIM(署名)・DMARC(整合性と失敗時の方針)を正しく設定することが基本です。DMARCはp=noneから始めてレポートで送信元を棚卸しし、段階的にp=rejectへ進める。バウンスと苦情はWebhookで受け取って送信停止リストに反映し、Gmailの送信者ガイドラインを送信量にかかわらず満たしておく。この運用ができていれば、到達率のトラブルはほぼ防げます。
まずはdigで自社ドメインのSPF・DMARCレコードを確認し、DMARCが未設定ならp=noneでレポート受信を始めてみてください。メール送信基盤の構築やDMARC導入の伴走が必要な場合は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します