WordPressからメールが届かない時の対処法(送信できているか確認)

実測 に検証

問い合わせフォームの通知が来ない。パスワード再設定のメールも来ない。

この症状は2 つの全く違う状態に分かれます。切り分けないまま SMTP プラグインを入れたり SPF を設定したりしても、原因が別なら直りません。

状態意味直す場所
wp_mail()false を返しているWordPress が送信すらしていないWordPress 側の設定
wp_mail()true を返している送信はした。届いていないだけサーバー・DNS・受信側

先にどちらかを確定させます。

見分け方: 画面の文言で分かる

「パスワードをお忘れですか?」からリセットを要求すると、 WordPress が結果をそのまま画面に出します。実測した 2 つの状態です。

送信できていない送信できた
POST 後の HTTP200302
画面エラー: メールを送信できませんでした。サイトのメール送信が正しく設定されていない可能性があります。「確認のリンクを含むメールを送信しました」
実際の受信0 通1 通

エラー: メールを送信できませんでした。サイトのメール送信が正しく設定されていない可能性があります。

送信できていない状態。WordPress がメールを送っていない

メールの確認リンクをクリックし、ログインページを訪問してください。

送信できた状態。ここから先で届かないなら WordPress の外の問題

「送信できませんでした」と出ているなら wp_mail() は false です。 この場合、SPF や DKIM をいくら設定しても意味がありません。

「送信しました」と出ているのに届かない場合は、WordPress 側は成功しています。 原因はサーバーの送信経路か、受信側の迷惑メール判定です。

wp_mail() が false になる代表的な原因: 送信元アドレス

WordPress の既定の送信元は wordpress@<サーバー名> です。 サーバー名にドットが含まれていないと、PHPMailer が不正なアドレスと判定して 送信を中止します。

実測したエラーです。

wp_mail_failed: 無効なアドレス:  (From): wordpress@localhost
wp_mail: false

localhost やコンテナのホスト名のように、ドットのないホスト名の環境で起きます。 共用サーバーでは起きにくいですが、VPS・Docker・社内サーバーでは頻発します。

対処は送信元アドレスを明示的に差し替えることです。

add_filter( 'wp_mail_from', function () {
	return 'no-reply@example.com';   // 実在するドメインにする
} );
add_filter( 'wp_mail_from_name', function () {
	return 'サイト名';
} );

ドメインは実在するものにします。存在しないドメインを送信元にすると、 今度は受信側で弾かれます(wp_mail() は true を返すので、 症状が「届かないだけ」に変わってしまい、原因が見えにくくなります)。

エラーの中身を取る

wp_mail() は false しか返さないので、理由を取るには wp_mail_failed フックを使います。原因の特定にはこれが最短です。

// mu-plugins に置く
add_action( 'wp_mail_failed', function ( $error ) {
	error_log( '[wp_mail_failed] ' . $error->get_error_message() );
} );

wp-content/debug.log に理由が出ます。実測では 無効なアドレス: (From): wordpress@localhost が記録されました。

測り方を間違えると誤診する

これは検証中に自分が踏んだ落とし穴です。

WP-CLI から wp_mail() を呼んで確認したところ false が返りました。 しかし原因はまったく別でした。

sendmail: can't connect to remote host (127.0.0.1): Connection refused
wp_mail: false

WP-CLI は Web サーバーとは別の環境で動いていることがあります(この検証環境では 別コンテナ)。そちらに送信経路が設定されていなければ、当然 false になります。 Web 経由では同じ設定で正常に送信できていました。

つまり、メールの検証は実際の経路(Web からの操作)で行う必要があります。 コマンドラインでの結果をそのまま信じると、直っているものを壊しに行きます。

同じことは共用サーバーでも起きます。cron から動かしたときだけ送れない、 管理画面からは送れる、という差はこの層の違いです。

wp_mail() が true なのに届かない場合

WordPress は無罪です。見る場所は 3 つです。

1. 送信元ドメインが実在し、SPF が通っているか

no-reply@自分のドメイン にしておき、そのドメインの SPF レコードに サーバーの送信元を含めます。サーバーの共有 IP が他利用者のせいで ブロックされていることもあるため、これが疑わしいなら次へ進みます。

2. SMTP を使う

PHP の mail() 関数を使わず、外部の SMTP サービス経由に切り替えます。 送信の成否とログがサービス側に残るので、切り分けが一気に楽になります。 「届かない」を調べる作業が「送信ログを見る」作業に変わります。

3. 受信側の迷惑メール判定

Gmail 宛だけ届かない、などの偏りがあればこれです。 受信側の迷惑メールフォルダと、ドメインの DMARC を確認します。

フォームの通知が来ない場合の追加確認

問い合わせフォームに限っては、メール以前の問題であることがあります。

  • 送信自体が失敗している(キャッシュに nonce が焼き込まれて期限切れ)
  • 管理画面のフォーム設定で宛先が空・誤り
  • スパム対策プラグインが送信を破棄している

まず「フォームの送信記録が残っているか」を確認します。 記録があるならメールの問題、無いなら送信の問題です。 記録を残す機能がないフォームプラグインなら、先に プラグインの競合 の観点で キャッシュとの組み合わせを確認します。

なお現行の Contact Form 7 では、未ログインの訪問者向けに nonce を出していないため、 nonce の期限切れは原因になりません。送信ボタンが止まる場合は → Contact Form 7 で送信できない

確認の順番

  1. 「パスワードをお忘れですか?」でリセットを要求する — 画面の文言で wp_mail() の成否が分かる
  2. 「送信できませんでした」 → wp_mail_failed フックで理由をログに出す → 送信元アドレスを疑う
  3. 「送信しました」なのに届かない → SPF / SMTP / 受信側
  4. 確認は必ず Web 経由で行う(コマンドラインの結果は別経路のもの)

再現手順

この検証環境では Mailpit(受信箱)を持っているので、 「WordPress は送ったのか」を確実に分離できます。

# 受信箱を空にする
curl -s -X DELETE "http://localhost:8025/api/v1/messages"

# パスワード再設定を要求(Web 経由)
curl -s -X POST "http://localhost:8080/wp-login.php?action=lostpassword" \
  --data-urlencode "user_login=admin" --data "wp-submit=send" -o /tmp/lp.html -w '%{http_code}\n'

# 届いたか
curl -s "http://localhost:8025/api/v1/messages?limit=1"

# 送信元の修正を外すと再現する
mv src/wp-content/mu-plugins/lab-mail.php /tmp/
# → HTTP 200・受信 0 通・画面に「メールを送信できませんでした」
mv /tmp/lab-mail.php src/wp-content/mu-plugins/