「昨日の夕方に送ったメールが、相手に届いていないそうです。調べてもらえますか」
メールサーバーの担当者なら、必ず受ける問い合わせです。答えはログとキューにあります。ただし、読み方を知らないと、大量のログの中で迷子になります。
第8回では、広く使われているMTAのPostfixを例に、構成、設定、オープンリレーを作らないリレー制御、キューとログの読み方、そして送信専用サーバーの組み方を見ていきます。
Postfixの構成
Postfixは、役割ごとに分かれた小さなデーモンの集まりです。常駐するmasterが、必要に応じて各デーモンを起動します。

- 入口:外部からSMTPで届いたメールは
smtpdが受け取り、サーバー上のsendmailコマンドで投入されたメールはmaildropキューを経てpickupが拾う - 整形:どちらも
cleanupでヘッダーの補完(Message-ID・Date等)やアドレスの書き換えを受け、incomingキューに1つのファイルとして置かれる - 出口:
qmgr(キューマネージャー)が配送の順番を管理し、外部宛てはsmtpに、ローカルのメールボックス宛てはlocal(またはvirtual・lmtp)に配送を頼む。失敗したメールはdeferredキューで再送を待つ
最小の権限で動くプロセスに分けるのは、脆弱性の被害を広げないためです。
キューの種類
| キュー | 入るメール | 次の動き |
|---|---|---|
| maildrop | sendmail コマンドでローカルから投入されたもの | pickup が拾って cleanup へ渡す |
| incoming | cleanup が整えた到着直後のメール | qmgr が active へ移す |
| active | 配送中のメール(数に上限がある) | 成功で削除、一時失敗で deferred、恒久失敗でバウンス |
| deferred | 一時エラーで配送できなかったメール | 間隔を空けて再試行。期限を過ぎたらバウンス |
| hold | 管理者が保留にしたメール | postsuper -H で戻すまで配送しない |

- 再試行の間隔は
minimal_backoff_time(既定300秒)からmaximal_backoff_time(既定4000秒)まで延び、maximal_queue_lifetime(既定5日)を過ぎると送信者にエラーメールを返す(第2回) - 遅延の通知(
delay_warning_time)は既定で無効 - キューのファイルは直接消さず、この後のコマンドで操作する
一時失敗は消えずに、deferredとactiveを行き来して待つのです。
main.cfの主な設定
| パラメーター | 意味 | 既定値 | 例(送信専用サーバー) |
|---|---|---|---|
| myhostname | 自分の FQDN。EHLO・Received に使う | OS のホスト名 | app01.example.jp |
| mydomain | 自分のドメイン | myhostname の先頭の部分を除いたもの | example.jp |
| myorigin | ローカルで出したメールに付けるドメイン | $myhostname | $mydomain |
| mydestination | 自分が最終配送先になるドメイン | $myhostname, localhost.$mydomain, localhost | 空(ローカル配送しない) |
| mynetworks | リレーを許す「信頼する」接続元 | 自ホストだけ(3.0 以降) | 127.0.0.0/8 [::1]/128 |
| relayhost | 外部宛てを渡す次のサーバー(スマートホスト) | 空(MX へ直接配送) | [relay.example.jp]:587 |
| inet_interfaces | 受け付けるネットワークインターフェース | all | loopback-only |
mydestinationにドメインを入れると、そのドメイン宛てをローカルのユーザーとして配送しようとする。Microsoft 365などに本物のメールボックスがあるドメインを入れてしまい、外に出ず「User unknown」になるのは典型的な誤り[ ]で囲んだrelayhostは、MXを引かずにその名前へ直接送る
postconfで設定を確かめる
$ postconf -n # 既定値から変えた設定だけを表示(抜粋)
compatibility_level = 3.10
inet_interfaces = loopback-only
mydestination =
myhostname = app01.example.jp
relayhost = [relay.example.jp]:587
$ postconf -d mynetworks_style # 既定値を表示
mynetworks_style = host
$ postconf mail_version # 版を確認
mail_version = 3.10.6
$ sudo postconf -e 'relayhost = [relay.example.jp]:587' # main.cf を書き換える
$ sudo postfix check && sudo systemctl reload postfix # 検査して反映
- 設定の確認は、main.cfを目で読むより
postconf -nが確実。同じパラメーターを2回書いた場合も、実際に効いている値が分かる postconf -Mでmaster.cfのサービスの一覧、postconf -Pでmaster.cfの-oによる上書きを表示する。3.11からは-jでJSONでも出力できるcompatibility_levelは、版を上げても古い既定値のまま動かす安全装置。上げると既定値が変わる(例:3.11でsmtp_tls_security_levelの既定がmayになる)ため、ログの警告とpostconf -nを確かめてから上げる
master.cf:submissionとsubmissions
# service type private unpriv chroot wakeup maxproc command + args
smtp inet n - n - - smtpd
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_tls_auth_only=yes
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
submissions inet n - n - - smtpd
-o syslog_name=postfix/submissions
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
- master.cfはどのポートでどのデーモンを動かすかを決める。
-oはそのサービスだけmain.cfの設定を上書きする(抜粋。公式の既定ファイルにはほかの-oもある) - submission(587)はSTARTTLSを必須にし、認証したクライアントだけに送信を許す。submissions(465)は接続直後からTLSで話す(
smtpd_tls_wrappermode=yes、第4回) - 既定のmaster.cfではどちらもコメントになっている。行頭の
#を外し、SASL認証(Dovecot等)の設定と合わせて有効にする(Dovecot 側の設定は連載「POP・IMAP入門」第11回(サーバーの構築と運用 前編))。古い版やディストリビューションの既定ファイルでは、465のサービス名がsmtpsのことがある - 変更後は
postfix reload。ss -ltnp | grep masterで25・587・465を待ち受けていることを確かめる
リレーの制御:オープンリレーを作らない
オープンリレーとは、誰からのメールでも、どこ宛てでも中継してしまうメールサーバーです。見つかるとすぐにスパムの踏み台にされ、DNSBLに載って(第7回)自社のメールまで届かなくなります。

- Postfixが中継(自分が最終配送先でないドメイン宛て)を許す条件は
smtpd_relay_restrictionsで決まる。既定値はpermit_mynetworks, permit_sasl_authenticated, defer_unauth_destination - mynetworksの接続元か、SMTP AUTHで認証したクライアントでなければ、担当外のドメイン宛ては断る
- 危ないのは、mynetworksに「とりあえず」広い範囲を入れることや、接続元がすべてロードバランサーの内部IPに見える構成で、そのIPを入れること
許可は、送信元のIPだけを列挙します。
リレー制御の設定と確認
# /etc/postfix/main.cf(抜粋)
mynetworks = 127.0.0.0/8 [::1]/128 192.0.2.21/32 192.0.2.22/32
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated,
reject_unauth_destination
# 許可していない IP から試す:他ドメイン宛ては拒否されるのが正しい
$ nc -C relay.example.jp 25
220 relay.example.jp ESMTP Postfix
EHLO test.example.com
(250- の行は略)
MAIL FROM:<test@example.com>
250 2.1.0 Ok
RCPT TO:<someone@example.org>
554 5.7.1 <someone@example.org>: Relay access denied
- 既定の
defer_unauth_destinationでは454 4.7.1(一時エラー)、reject_unauth_destinationでは554 5.7.1(恒久エラー)で断る - 受信側のログには
NOQUEUE: reject: RCPT from unknown[198.51.100.7]: 554 5.7.1 <someone@example.org>: Relay access denied; …と出る - 確認は必ず自分が管理するサーバーに対して行う。他組織のサーバーをオープンリレーか試すのは迷惑行為になる。Windowsからは、第9回のPowerShellの方法で同じ確認ができる
キューの操作
| コマンド | 動作 | 使いどころ |
|---|---|---|
| postqueue -p(mailq) | キューの一覧と、遅延の理由を表示 | まず状況を見る |
| postqueue -f | キューのメールをすべて今すぐ再送 | 相手の復旧後 |
| postqueue -i キューID | 指定の1通を今すぐ再送 | 1通だけ試す |
| postsuper -d キューID | 指定の1通を削除(-d ALL deferred で deferred を全部削除) | 誤送信・スパムの滞留 |
| postsuper -h キューID/-H | 保留にする/保留を解く | 調べる間だけ止める |
| postcat -q キューID | キューのファイルの中身(エンベロープ・ヘッダー・本文)を表示 | 送信者・宛先・内容の確認 |
$ postqueue -p
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
A3D5F20C41 2210 Sat Sep 26 09:02:44 alert@example.jp
(connect to mx.example.org[203.0.113.25]:25: Connection timed out)
user@example.org
-- 2 Kbytes in 1 Request.
キューIDの直後の*は配送中(active)、!は保留中(hold)を表します。括弧の中が最後の失敗の理由です。postsuper -d ALLはキューのメールをすべて消すため、スパムの滞留の後始末などを除いて使いません。
ログの読み方① キューIDで1通を追う
冒頭の「昨日送ったメールが届いていない」を調べる方法です。
$ grep 8C1E0A1B2F /var/log/maillog # RHEL 系(Debian 系は /var/log/mail.log)
Sep 26 10:15:01 relay postfix/smtpd[2345]: 8C1E0A1B2F: client=app01.example.jp[192.0.2.21]
Sep 26 10:15:01 relay postfix/cleanup[2346]: 8C1E0A1B2F: message-id=<20260926011501.4F2A@app01.example.jp>
Sep 26 10:15:01 relay postfix/qmgr[1101]: 8C1E0A1B2F: from=<alert@example.jp>, size=1532, nrcpt=1 (queue active)
Sep 26 10:15:02 relay postfix/smtp[2347]: 8C1E0A1B2F: to=<user@example.com>, relay=mx.example.com[198.51.100.25]:25, delay=1.2, delays=0.05/0.01/0.62/0.52, tls=may, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 4ZbQ1x2Kp3z9)
Sep 26 10:15:02 relay postfix/qmgr[1101]: 8C1E0A1B2F: removed
$ journalctl --facility=mail --since today | grep 8C1E0A1B2F # journal だけの環境

- Postfixは1通ごとにキューIDを振り、受け取りから削除まで同じIDでログに出す。キューIDでgrepすれば1通の一生が分かる
- 行の順:smtpd(接続元)→ cleanup(Message-ID)→ qmgr(エンベロープの送信者・宛先の数)→ smtp(配送の結果)→ qmgr(removed)。from・toはエンベロープの値(第3回)
queued asの後ろは相手側のキューID。相手の管理者に伝えれば、その先を追ってもらえる。tls=mayは3.11以降で出るTLSの使われ方の表示- ログの場所:RHEL系は
/var/log/maillog、Debian・Ubuntu系は/var/log/mail.log。rsyslogを入れない環境(Debian 12以降の既定など)ではjournalにだけ残る(連載「syslog入門」)
Message-IDしか分からないときは、まずMessage-IDでgrepしてキューIDを見つけ、次にキューIDでgrepします。
ログの読み方② statusの読み方
| 表示 | 意味 | 次に確認すること |
|---|---|---|
| status=sent | 相手が 250 で受け取った | 届かないなら相手側(迷惑メール判定・転送)を調べる |
| status=deferred | 一時的な失敗。キューに残り再送する | 括弧内の理由(接続できない・4xx の応答) |
| status=bounced | 恒久的な失敗。送信者にエラーメールを返す | 括弧内の相手の応答(5xx と拡張ステータスコード) |
| status=expired, returned to sender | 再送を続けたが期限(既定5日)が切れた | それまでの deferred の理由をさかのぼる |
| NOQUEUE: reject: | 受信側(smtpd)が受け取りを拒否した | 拒否の理由と接続元・from・to |
| delays=a/b/c/d | a:キューマネージャーに渡るまで(受け取りを含む)、b:キューマネージャーの中での待ち、c:接続の確立(DNS・EHLO・TLS を含む)、d:メッセージの送信 | どの段階で時間がかかったか |
… to=<nosuch@example.com>, relay=mx.example.com[198.51.100.25]:25, …, dsn=5.1.1, status=bounced
(host mx.example.com[198.51.100.25] said: 550 5.1.1 <nosuch@example.com>: Recipient address rejected: User unknown (in reply to RCPT TO command))
… to=<user@example.org>, relay=none, delay=300, …, dsn=4.4.1, status=deferred
(connect to mx.example.org[203.0.113.25]:25: Connection timed out)
status=sentは「相手が受け取った」までで、「受信箱に入った」ではありません。sentなのに届かないなら、相手側の迷惑メール判定や転送を疑います。
(in reply to RCPT TO command)は、相手がどのコマンドに対して断ったかを示します。RCPT TOなら宛先の問題、end of DATA commandなら内容の問題(迷惑メール判定・DMARCの不合格など)であることが多いです(第11回)。
TLSの設定
# 受信側(smtpd)
smtpd_tls_chain_files = /etc/postfix/tls/mail.example.jp.pem # 鍵→証明書→中間証明書の順
smtpd_tls_security_level = may # 公開の25番では may(必須にしない)
smtpd_tls_auth_only = yes # 暗号化の前は AUTH を出さない
smtpd_tls_received_header = yes # Received に TLS の情報を書く
smtpd_tls_loglevel = 1
# 送信側(smtp)
smtp_tls_security_level = may # 3.11 以降(compatibility_level 3.11)は既定で may
smtp_tls_loglevel = 1
smtp_tls_policy_maps = lmdb:/etc/postfix/tls_policy # 宛先ごとに encrypt・secure 等を指定
smtp_tls_security_level:none(使わない)/may(日和見的TLS)/encrypt(暗号化は必須、証明書は検証しない)/dane(DNSSECのTLSAに従って検証、無ければmay)/verify・secure(証明書と名前を検証)- インターネットへの配送はmay(またはdane)を既定にする。encrypt以上を全体の既定にすると、TLS非対応の相手に届かなくなる。リレー先や取引先など決まった相手だけ、
smtp_tls_policy_mapsでencrypt・secureにする - ログには
Trusted TLS connection established to …: TLSv1.3 with cipher …のように、相手の証明書の信頼の程度(Anonymous・Untrusted・Trusted・Verified)が出る。MTA-STS・DANEは第4回、証明書の用意は連載「証明書入門」で扱っている
送信専用サーバーとスマートホスト
アプリケーションサーバーや監視サーバーのように、メールを送るだけのサーバーでは、Postfixを送信専用(null client)にします。

- 外からのメールを受け取らないよう
inet_interfaces = loopback-only、ローカル配送をしないようmydestinationを空にし、外部宛てはすべてrelayhostのスマートホスト(社内のリレーサーバーやクラウドの送信サービス)に渡す - 各サーバーはMXを引かず、外向きの25番も要らなくなる。SPF・DKIM・逆引きといった外向けの整備を、スマートホストに集約できる
- スマートホストが認証を求める場合は、SASLのクライアント設定(
smtp_sasl_auth_enable・smtp_sasl_password_maps)でIDとパスワードを渡し、必ずTLSと組み合わせる
台数が増えても、外から見える送信元は1つのままです。
Microsoft 365・SESへのリレー設定の例
# Amazon SES:587番の STARTTLS+SMTP 認証(AWS のドキュメントの手順に準拠)
relayhost = [email-smtp.ap-northeast-1.amazonaws.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_tls_security_level = secure
# /etc/postfix/sasl_passwd(chmod 600 にしてから postmap hash:/etc/postfix/sasl_passwd)
[email-smtp.ap-northeast-1.amazonaws.com]:587 SMTPユーザー名:SMTPパスワード
# Microsoft 365 の SMTP リレー:受信コネクターで IP か証明書を認証(パスワード不要)
relayhost = [example-jp.mail.protection.outlook.com]:25
smtp_tls_security_level = encrypt
- SESのSMTPの認証情報は、IAMのアクセスキーとは別物。リージョンのSMTPエンドポイントを使い、送信元の検証とサンドボックスの解除が要る(第10回、連載「クラウド入門」第14回)。Berkeley DBを外したディストリビューションでは、
hash:の代わりにlmdb:等を使う(postconf -mで確認) - Microsoft 365へのリレーは、受信コネクター(固定のパブリックIP、または受け入れ済みドメインを含む証明書で認証)を作り、MXのエンドポイントへ25番で送る。送信元は受け入れ済みドメインのアドレスにし、SPFに送信元IPを加える。証明書で認証する場合は、
smtp_tls_chain_filesでクライアント証明書を送る smtp.office365.com:587のSMTP AUTHは、Basic認証が2026年12月末から既定で無効になる(第4回)。Postfixは標準ではOAuth 2.0(XOAUTH2)で認証できないため、コネクター方式やSES等を選ぶ(第10回)
WindowsでのSMTPとPostfixの現行版
| 項目 | 状況(2026年9月) | 対応 |
|---|---|---|
| IIS の SMTP サーバー(IIS 6 由来の機能) | Windows Server 2025 で削除。Windows Server 2022 が最後の版 | 新しく使わない。既存は移行を計画する |
| IIS SMTP から Microsoft 365 へのリレー | Microsoft はサポート対象外としている | Exchange Server、Linux の Postfix、クラウドの送信サービスへ移す |
| Windows からのテスト送信 | Send-MailMessage は非推奨 | PowerShell の TcpClient で確かめる(第9回) |
| Postfix の現行版 | 3.11.7(2026年9月7日)。更新の対象は 3.8〜3.11 | postfix.org の告知で確認する |
| ディストリビューションの版 | 例:Ubuntu 26.04 は 3.10.6 | postconf mail_version。修正はパッケージの更新で入る |
2026年9月の3.11.7と各版の更新には、遠隔からのサービス停止やSMTPスマグリング(改行の扱いの違いを突いてメールを紛れ込ませる攻撃)につながる不具合の修正が含まれます。MTAはインターネットに直接さらされるため、ディストリビューションのセキュリティ更新を定期的に当て、更新の対象から外れた版(3.7以前)を使い続けないようにします。
考えてみよう
- 社内のリレーサーバーで
postconf -nを実行し、mynetworksに入っている範囲をすべて説明できますか。不要に広い範囲はありませんか。 - 「昨日の夕方に送ったメールが届いていない」と言われました。Message-IDしか分からない場合、どのコマンドの順でログを追いますか。
- アプリケーションサーバー20台が、それぞれ直接インターネットへメールを送っています。送信専用+スマートホストの構成に変えると、何が楽になり、何に注意が要りますか。
ヒント:この記事のpostconf、リレーの確認、キューIDでログを追う方法、送信専用サーバーの図が答えにつながります。
まとめ
- Postfixは役割ごとのデーモンの集まり。入口はsmtpdとpickup、出口はsmtpとlocal
- 一時失敗はdeferredで再送を待ち、既定5日で期限切れ。キューはpostqueue・postsuperで操作する
- 設定の確認は
postconf -n。mydestinationに本物のメールボックスがあるドメインを入れない - 中継はmynetworksかSMTP AUTHの相手にだけ許す。外から試してRelay access deniedを確かめる
- ログはキューIDでgrepして1通を追う。
status=sentは「相手が受け取った」まで - 送るだけのサーバーは送信専用+スマートホストにし、外向けの整備を集約する
- MTAはインターネットにさらされる。セキュリティ更新を定期的に
次回は、MTAの外側から、dig・nc・openssl・PowerShellでメールの経路を調べる方法を見ていきます。
連載「メール入門」全12回
- メールが届くまで
- SMTPのやり取り
- メッセージの構造
- ポートと暗号化・認証
- 送信ドメイン認証 前編
- 送信ドメイン認証 後編
- 迷惑メール対策と配信
- MTAの運用(この記事)
- コマンドで調べる
- クラウドと現代のメール
- メールのトラブルシュート
- メールの設計演習

コメント