メール送信について正しい仕組みを整理するためにメモしておこうと思います。
例としてPHPを使用していますが、どの言語でも問題はないかと思います。
メール送信の基本的な流れ
メールが送信されて相手に届くまでには、いくつかのステップを経ます。
1. メール送信(SMTP - ポート587/465)
パソコンやプログラムからメールを送信すると、まず送信者側のSMTPサーバーにメールが届けられます。いきなり相手に届くわけではなく、まず自分側のメールサーバーに一時的に保管されるのです。これは実際の郵便と同じでまず最寄りの郵便局に郵送物が預けられる仕組みに似ています。
あなたのPC/プログラム
↓ (SMTP - ポート587または465)
送信者側SMTPサーバー(プール)
この最初のステップで使われるプロトコルがSMTP(Simple Mail Transfer Protocol)で、一般的にはポート587が使われます。このポートでは**認証(ユーザー名・パスワード)**が必要です。
重要な補足: かつてはポート25が使われていましたが、スパムメールの温床となったため、現在では認証付きのポート587が標準となっています。
2. メールサーバー間の配送(SMTP - ポート25)
送信者側のSMTPサーバーに保管されたメールは、次に受信者側のメールサーバーへ配送されます。たとえていうと最寄りの郵便局から送付先の最寄りの郵便局に届けられるようなイメージです。
送信者側SMTPサーバー
↓ (SMTP - ポート25)
↓ (DNSでMXレコードを検索)
↓
受信者側メールサーバー
この配送でもSMTPプロトコルが使われますが、ポート番号は25です。サーバー間通信では基本的に認証は不要です(ただしスパム対策でSPF/DKIMなどの検証は行われます)。
配送先の特定方法: 受信者のメールアドレス(例: user@example.com)から、example.comのドメインを抽出し、DNS問い合わせをおこなってMXレコード(Mail Exchangeレコード)を取得します。これによって「どのメールサーバーに配送すればいいか」がわかります。
3. メール受信(POP/IMAP)
受信者側のメールサーバーに届いたメールは、受信者がメールクライアントでアクセスすることで取得できます。受け取り側の最寄りの郵便局から受け取り手に郵送物が届いたようなイメージです。
受信者側メールサーバー
↓ (POP: ポート110 または IMAP: ポート143)
受信者のメールクライアント
受信時のプロトコルには主に2種類があります:
- POP3(ポート110): メールをダウンロードして端末に保存。Outlookなどの従来型メーラーで使用
- IMAP(ポート143/993): メールをサーバーに置いたまま閲覧。Gmailなどのクラウド型サービスで使用
メール送信時の暗号化 - TLS/SSLの重要性
メール送信では、認証情報(パスワード)やメール内容が盗聴されないよう、通信を暗号化することが必須です。暗号化には主に2つの方式があります。
STARTTLS方式
通常ポート: 587
仕組み:
- 最初は暗号化されていない状態で接続
- 接続後に「STARTTLS」コマンドを送信
- サーバーが対応していれば、その場で暗号化通信に切り替わる
- 暗号化された状態で認証とメール送信を行う
クライアント → サーバー (平文接続)
クライアント: "STARTTLS"
サーバー: "220 Ready to start TLS"
↓ ここから暗号化通信に切り替わる
クライアント: ユーザー名・パスワード送信(暗号化済み)
メリット:
- 標準的で広く対応されている
- 暗号化に失敗した場合、平文通信にフォールバックできる(設定次第)
- ファイアウォールで通常のSMTPポートとして扱える
PHPMailerでの設定例:
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
SSL/TLS方式(SMTPS)
通常ポート: 465
仕組み:
- 接続する瞬間から暗号化通信
- 最初から最後まで完全に暗号化された状態
- HTTPSと同じような仕組み
クライアント → サーバー (最初から暗号化接続)
↓ 最初から暗号化された状態
クライアント: ユーザー名・パスワード送信(暗号化済み)
メリット:
- 最初から暗号化されるため、理論的にはより安全
- 接続開始時点から盗聴を完全に防げる
デメリット:
- 一部の古いファイアウォールで問題が起きることがある
PHPMailerでの設定例:
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;
どちらを使うべきか?
- RFC 8314では両方とも推奨されている
- ほぼすべてのメールサービスが対応している
- ネットワーク機器との互換性が高い
- Gmailなどの主要サービスも587を推奨(歴史的によく使われてきた)
暗号化なし(平文通信)は絶対NG
// ❌ 絶対にやってはいけない
$mail->SMTPSecure = '';
$mail->Port = 25;
暗号化なしでメール送信すると:
- パスワードが平文でネットワークを流れる
- メール内容が盗聴される可能性
- 多くのメールサーバーが接続を拒否する
開発環境でも必ず暗号化を使用しましょう。
メール送信時のよくあるエラー
実際の運用では、以下のようなトラブルが発生することがあります。
配送エラーの種類
- 配送先メールサーバーが応答しない: サーバーダウンやネットワーク障害
- 宛先のメールアドレスが存在しない: タイポや既に削除されたアカウント
- 宛先のメールボックスが満杯: 容量制限に達している
これらのエラーが発生すると、送信者にバウンスメール(配送失敗通知)が返されます。
ロリポップ等のレンタルサーバーでメールが送れる理由
昔、プログラミングを勉強し始めのころ、ロリポップなどのレンタルサーバーでmb_send_mail()を使ってメール送信をしていました。これは裏側で以下のような処理が行われているからです:
<?php
mb_send_mail('to@example.com', '件名', '本文');
内部で起きていること:
- PHPの
mb_send_mail()が呼ばれる - サーバーに設定済みのSMTPサーバー(ポート587) を自動的に経由する
- 認証情報(ID・パスワード) も自動的に使用される
- 暗号化通信(STARTTLS) も自動で有効化される
- 相手の受信メールサーバーに配送される
つまり、レンタルサーバー側が「すでに正しく設定されたSMTPサーバー」を用意してくれているので、開発者は何も意識せずにメールが送れるわけです。
一方、他の環境では、同じコードを実行してもメールが送れません。
問題点1: SMTPサーバーが存在しない
mb_send_mail()は内部的にmail()関数を呼び出しますが、この関数はローカルのMTA(Mail Transfer Agent) を必要とします。
# ローカル環境では通常インストールされていない
$ which sendmail
command not found
問題点2: 認証機能がない
mail()関数にはSMTP認証機能がありません。現代のメールサーバーは必ず認証が必要なので、認証なしで送信しようとすると拒否されます。
問題点3: 暗号化機能がない
mail()関数はTLS/SSL暗号化に対応していません。セキュリティ上の理由から、暗号化なしの接続は多くのメールサーバーで拒否されます。
問題点4: スパム判定される
仮に送信できても、以下の理由でスパム扱いされる可能性が高いです:
- SPF(Sender Policy Framework)レコードが設定されていない
- DKIM(DomainKeys Identified Mail)署名がない
- DMARC(Domain-based Message Authentication)ポリシーが未設定
- 送信元IPアドレスの評判が低い
問題点5: ISPによるポート25のブロック
多くのISP(インターネットサービスプロバイダ)は、スパム対策としてポート25への直接接続をブロックしています。
正しいメール送信方法
正しいメール送信方針としては下記のような手法が考えられます。
方法1: SMTPライブラリを使う(推奨)
mb_send_mail()を使わず、PHPMailerなどのライブラリで外部SMTPサーバーに接続します。
<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
// SMTPサーバー設定(Gmailの例)
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = 'your-email@gmail.com';
$mail->Password = 'your-app-password'; // アプリパスワード
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // STARTTLSを使用
$mail->Port = 587;
// メール内容
$mail->setFrom('from@example.com', '送信者名');
$mail->addAddress('to@example.com', '受信者名');
$mail->Subject = 'テストメール';
$mail->Body = 'これは本文です';
$mail->send();
echo 'メール送信成功';
これはOutlookなどのメーラーで行う作業をPHPで行っているだけと考えるとわかりやすいでしょう。メーラーもSMTPサーバー設定とID・パスワードが必要ですよね。それと同じです。
ポート465を使う場合:
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; // SSL/TLSを使用
$mail->Port = 465;
方法2: 開発用メールキャッチャーを使う
実際にメールを送信せず、開発環境内で確認するツールです。
MailHog(Dockerで簡単に起動):
docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog
これでhttp://localhost:8025にアクセスすると、送信したメールがWeb UIで確認できます。
PHPからMailHogを使う設定:
$mail->isSMTP();
$mail->Host = 'localhost';
$mail->Port = 1025;
$mail->SMTPAuth = false; // MailHogは認証不要
$mail->SMTPSecure = ''; // 開発環境なので暗号化不要
方法3: Mailtrapなどのサービスを使う
開発・テスト専用のメールサービスを利用します。実際のメールは送信されませんが、送信処理のテストが安全にできます。
まとめ
メール送信で覚えておくべきポイント:
基本的な流れ:
- 送信者 → SMTPサーバー(ポート587/465・認証あり・暗号化あり) → 相手のメールサーバー(ポート25) → 受信者
暗号化方式:
- STARTTLS(ポート587): 接続後に暗号化に切り替える。現在の標準
- SSL/TLS(ポート465): 最初から暗号化。一部のサービスで使用
- 暗号化なし: 絶対に使用しない
解決策:
- PHPMailerなどで外部SMTPサーバーを使う
- 必ずSTARTTLS(587)またはSSL/TLS(465)で暗号化する
- 開発環境ではMailHogなどのツールを使う
メール送信は「メーラーでやっていることをコードで再現する」と考えると理解しやすいです。設定もメーラーと同じように、SMTPサーバー・ポート・暗号化方式・認証情報を指定すればOKです。
特に暗号化は必須です。セキュリティ上の理由だけでなく、現代のメールサーバーのほとんどが暗号化なしの接続を拒否します。開発環境でも本番環境でも、必ずSTARTTLSまたはSSL/TLSを有効にしましょう。