問い合わせフォームから送信すると、「送信が完了しました」という画面が出ます。多くの方は、それで相手にメールが届いたと考えると思います。
実際には、その画面はメールが届いたことを意味しません。最近、担当しているサイトで「完了画面は出るのに、メールが届いていない」ことが続けて起きました。原因は4つあり、どれも画面の上では何のエラーも出ていませんでした。
この記事では、その4つの原因と直し方、そして「届かなかったときに取りこぼさない」ための作りをまとめます。
お客様のお名前やサイト名は伏せています。いずれも、エックスサーバー(共用サーバー)上のPHPから、mb_send_mail() でメールを送る構成のフォームです。
「送信完了」は何を保証しているのか
フォームから送られたメールは、いくつかの受け渡しを経て受信箱に届きます。
PHPが「成功」を返すのは、サイトのサーバーがメールを受け取った時点です
PHPのマニュアルにも、mail() の戻り値について次のように書かれています。
メールの配送が受け入れられたかどうかが基準であることに注意しましょう。 メールが実際にあて先に届いたかどうかでは「ありません」。
完了画面は、この戻り値を見て出しているだけです。その先で落ちても、フォームの側には何も伝わりません。
ですから、「完了画面が出た」は確認になりません。確認になるのは、受け取る側の受信箱にメールがあることだけです。
1. 封筒の差出人を指定したら、外部に届かなくなりました
メールには差出人が2つあります。封筒に書く差出人(エンベロープ From)と、便箋に書く差出人(ヘッダーの From)です。受信箱に表示されるのは便箋の方で、封筒の方は配送の途中で使われます。
PHPでは、送信時に -f というオプションを付けると封筒の差出人を指定できます。マニュアルでも用例として紹介されている書き方で、エラーメールの戻り先を指定できるため、よく使われます。
ところが、2つのサイトで同じ週に、-f を付けて送ったメールだけが、外部のアドレス(Gmail)に届かなくなりました。同じサーバー、同じ送信元IPから、-f の有無だけを変えて送ると、付けた方は届かず、外した方は届きます。サーバー内のアドレスには、どちらも届いていました。
厄介だったのは、サーバー内のアドレスからGmailへの転送も一緒に止まっていたことです。転送されるメールは、元の封筒の差出人を引き継ぎます。そのため「転送の設定が外れたのでは」と、見当違いの場所を先に疑うことになりました。
-f を外したところ、フォームからの通知・自動返信・転送のすべてが届くようになりました。以前は付けたままでも届いていたので、途中でサーバー側の扱いが変わったと考えています。ただ、それを説明する公式の告知は見つけられていません。
ここで言いたいのは「-f を外すべき」ではありません。環境が変われば、正しかった設定が正しくなくなることがある、ということです。送信まわりの設定を変えたとき、そして何も変えていなくても定期的に、外部のアドレスで着信まで確かめることです。それ以外に気づく方法はありませんでした。
2. 転送したメールが、携帯で「なりすまし」扱いになりました
フォームの通知を、サーバーのアドレスから携帯のキャリアメール(au)へ転送していたところ、届きませんでした。エラーメールも返ってきません。
原因は、auの迷惑メールフィルターにあるなりすまし規制です。推奨とされている「高」の設定では、封筒の差出人と便箋の差出人が詐称されていると判定したメールや、認証できないメールを受け取りません。転送メールは、封筒の差出人(転送したサーバー)と便箋の差出人(元の送り主)が食い違うので、この判定に引っかかります。
これはauの案内にも書かれている、既知の挙動です。
パソコンあてのメールを転送してケータイで受信されている方は、転送メールがなりすましと判断され、受信できなくなります。この事象を回避するため、転送元のアドレスを「受信リスト」で「必ず受信」にしてください。
つまり対処は受け取る側で行います。注意したいのは、受信リストに登録するだけでは足りず、「必ず受信する」へのチェックが要ることです。auのよくある質問にも、次のようにあります。
「なりすまし規制」を設定している場合、「なりすまし」に判定されるアドレスを「受信リスト」に設定しても受信されないため、受信リストにて「必ず受信する」にチェックが必要です。
ここを取りこぼしやすいので、お客様に設定をお願いするときは、チェックの有無まで確認していただきました。
ただし、同じことはお客様への自動返信でも起こりえます。携帯のアドレスで問い合わせた方に「受け付けました」のメールが届かず、送れたのかどうか不安にさせてしまいます。問い合わせてくださった方に設定をお願いすることはできないので、こちらは送る側で直すしかありません。封筒と便箋の差出人をそろえ、送信元の認証が通る形で送ることが対策になります。
3. 差出人の名前に日本語を入れたら、届きませんでした
別のサイトでは、コマンドから試しに送ったメールは届くのに、フォームから送ったメールだけが届きませんでした。
違いは差出人の表示名でした。フォームの方は、From: 会社名 <info@…> のように、日本語の表示名をそのままヘッダーに入れていました。
メールのヘッダーは、もともとASCII文字だけで書く決まりです。日本語を入れるときは、=?UTF-8?B?…?= のような形に変換(MIMEエンコード)する必要があります。PHPでは mb_encode_mimeheader() がこの変換を行い、マニュアルにも、表示名を変換してからアドレスと組み合わせる用例が載っています。
ヘッダーの組み立て方を直し、日本語がそのまま入らないようにしたところ、フォームからのメールも届くようになりました。
Gmailのメール送信者のガイドラインにも、メールの形式について次の一文があります。
Internet Format Standard(RFC 5322)に準拠する形式でメールを作成します。
規格から外れたメールは、受け取ってもらえなくても文句が言えません。
4. テストを続けて送ったら、途中から届かなくなりました
3つ目の原因を調べている途中、1〜2分のあいだに10通以上のテストを送りました。すると、それまで届いていた形式のメールまで、途中から届かなくなりました。
このとき一度、「本文が複数行だと届かない(1行の短文だけ届く)」という結論にたどり着きかけています。実際には本文の形は関係ありませんでした。たまたま短文を送った回が、絞られる前だっただけです。
時間を空けてから1通だけ送ると、問題なく届きました。短い時間にまとめて送ったことで、送信が絞られていたと考えられます。送る側のサーバーか受け取る側か、どこで絞られたのかまでは確かめられていません。
届かないとき、つい何度も送って確かめたくなります。しかしそれ自体が新しい原因になり、切り分けを難しくします。テストは間隔を空けて1通ずつ、送った時刻を控えながら行うのが結局は早道でした。
取りこぼさないための作り
4つとも直し方は違いますが、共通しているのは、フォームの画面からは何も分からなかったことです。メールは、どこかで黙って落ちることがあります。それを前提に、次の3つを作りに入れています。
問い合わせの内容は、サーバーにも残します
メールを「唯一の記録」にしないことです。送信された内容をサーバー側にも保存しておけば、メールが落ちても問い合わせそのものは失われません。
1つ目の件では、フォームの送り先がサーバー内のアドレスだったため、外部への転送が止まっていたあいだの問い合わせも、サーバーの受信箱にすべて残っていました。取りこぼしが1件もないことは、そこで確認できました。このサイトのフォームも、送信内容をサーバーに保存しています。
差出人は自分のドメインにします
問い合わせた方のアドレスを差出人(From)にして送ると、返信ボタンでそのまま返せて便利に見えます。しかしこれは、他人のドメインを名乗って送ることになります。Gmailのメール送信者のガイドラインにも、次のようにあります。
From: ヘッダーと To: ヘッダーには実際の送信者と受信者のみを指定してください。
差出人はサイト自身のアドレスにして、問い合わせた方のアドレスは Reply-To に入れます。こうすれば、返信ボタンはそのまま使えます。
定期的に、外部の受信箱まで確かめます
1つ目の件は、サーバー側の変化で起きました。こちらが何も変えていなくても、届かなくなることがあるということです。
そこで、決まった曜日にフォームから自動でテストを送り、その結果を外部の受信箱で受け取るようにしました。結果のメールが届かなければ、それ自体が異常の知らせになります。フォームの画面が正常に動くかだけを見る検査では、今回の4つはどれも見つけられません。
DMARCは入れた方がいいのか
届かない話になると、SPF・DKIM・DMARCという送信ドメイン認証の話がよく出てきます。今回も、DMARCを入れるべきかを検討しました。
Gmailのメール送信者のガイドラインでは、メール認証の要件を次のように定めています。
すべての送信者: SPF または DKIM
一括送信者: SPF、DKIM、DMARC
すべての送信者に求められているのは、SPF か DKIM のどちらかです。DMARC まで必須になる一括送信者は、同じページで1日あたり5,000件以上のメールを送る送信者とされています。問い合わせフォームの通知と自動返信なら、多くの場合ここには当たりません。
なりすまし対策として DMARC を入れること自体は良いことです。ただ、今回の4つはどれも DMARC で解決するものではありませんでした。届かない原因を調べる前に認証の設定を足しても、直らないことは覚えておいて損はないと思います。
まとめ
- 「送信完了」の画面は、サイトのサーバーがメールを受け取ったことしか意味しません
- 封筒の差出人の指定(
-f)で、外部にだけ届かなくなることがありました。転送も巻き添えになります - 携帯のキャリアメールへの転送は、なりすまし規制で黙って捨てられることがあります。受け取る側で「必ず受信する」の設定が必要です
- ヘッダーに日本語を入れるときは、MIMEエンコードが必要です
- テストの連打は、それ自体が新しい原因になります
- 問い合わせの内容はサーバーにも残し、外部の受信箱で定期的に着信を確かめます
フォームは、作った日に動いていても、届き続ける保証はありません。問い合わせが来ないのは、本当に来ていないからなのか、届いていないからなのか。一度、外部のアドレスからご自身のフォームに送ってみてください。
確かめ方が分からない、届いていないかもしれないという場合は、お問い合わせからご相談ください。
参考資料
- mail — PHP マニュアル — 戻り値は「配送が受け入れられたか」であり「届いたか」ではないこと、
-fによる封筒の差出人の指定 - mb_encode_mimeheader — PHP マニュアル — ヘッダーに日本語を入れるときのMIMEエンコード
- メール送信者のガイドライン — すべての送信者と、1日5,000件以上の送信者に求められる要件(Google Workspace 管理者ヘルプ)
- なりすまし規制 | 迷惑メールフィルター機能 — 規制のレベルと判定の基準(au)
- オススメ設定 | 迷惑メールフィルター設定 — 転送メールがなりすましと判断される場合の対処(au)
- 受信リスト設定についてのよくある質問 — なりすましと判定されるアドレスは「必ず受信する」へのチェックが必要なこと(au)