記錄實際專案中從 mail() 失敗排查到改用 PHPMailer 成功發送的完整過程,包含常見錯誤訊息分析、Sendmail 設定、PHPMailer 完整配置與測試驗證。
<?php
mail('test@163.com', 'Test', 'Content');
// Warning: mail(): Failed to connect to mailserver at "localhost" port 25
// verify your "SMTP" and "smtp_port" setting in php.ini
?>
原因:本機未安裝/啟動 MTA (Sendmail/Postfix),或 php.ini 未正確設定。
嘗試修正 php.ini:
SMTP = smtp.163.com smtp_port = 25 sendmail_from = admin@163.com
結果:仍失敗,新錯誤:
Warning: mail(): SMTP server response: 553 authentication is required
根本原因:mail() 不支援 SMTP 認證。現代郵件伺服器(Gmail、163、Outlook 等)皆要求認證,mail() 無法提供帳號密碼,故無法直接使用外部 SMTP。
| 方案 | 可行性 | 說明 |
|---|---|---|
| 架設本地 Postfix/Sendmail 並設定 Relay | 可行但複雜 | 需精通 MTA 設定、SPF/DKIM、佇列管理 |
| 使用不需認證的內部 SMTP | 罕見 | 現代環境幾乎不存在 |
| 改用 PHPMailer (SMTP 類別) | ✅ 推薦 | 支援認證、SSL/TLS、HTML、附件、純 PHP 實作 |
composer require phpmailer/phpmailer # 或手動下載:https://github.com/PHPMailer/PHPMailer
<?php
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true);
try {
// SMTP 設定
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = 'your@gmail.com';
$mail->Password = 'your_16_char_app_password'; // 應用程式密碼
$mail->SMTPSecure = 'tls'; // 或 'ssl'
$mail->Port = 587; // TLS: 587, SSL: 465
// 寄件人/收件人
$mail->setFrom('your@gmail.com', '您的名稱');
$mail->addAddress('recipient@example.com', '收件人');
// 內容
$mail->isHTML(true);
$mail->Subject = 'PHPMailer 測試';
$mail->Body = '<h1>HTML 郵件</h1><p>這是 <b>測試</b> 內容。</p>';
$mail->AltBody = '純文字備用內容';
$mail->send();
echo '發送成功!';
} catch (Exception $e) {
echo "發送失敗: {$mail->ErrorInfo}";
}
?>
<?php $mail->Host = 'smtp.163.com'; $mail->Port = 465; // SSL $mail->SMTPSecure = 'ssl'; $mail->Username = 'your@163.com'; $mail->Password = 'client_auth_password'; // 客戶端授權碼,非登入密碼 ?>
<?php $mail->Host = 'smtp.yourcompany.com'; $mail->Port = 587; $mail->SMTPSecure = 'tls'; $mail->Username = 'user@yourcompany.com'; $mail->Password = 'your_ad_password'; ?>
<?php
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = 'sender@gmail.com';
$mail->Password = 'app_password';
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
// 字元編碼
$mail->CharSet = 'UTF-8';
// 寄件人
$mail->setFrom('sender@gmail.com', '發送系統');
$mail->addReplyTo('reply@gmail.com', '回覆處理');
// 收件人
$mail->addAddress('user1@example.com', '用戶一');
$mail->addAddress('user2@example.com'); // 僅信箱
$mail->addCC('cc@example.com', '抄送人');
$mail->addBCC('bcc@example.com', '密件副本');
// 附件
$mail->addAttachment('/path/to/report.pdf', '月報.pdf');
$mail->addAttachment('/path/to/data.xlsx');
// 嵌入圖片 (CID)
$mail->addEmbeddedImage('/path/to/logo.png', 'logo_cid');
$mail->addEmbeddedImage('/path/to/banner.jpg', 'banner_cid');
// 內容
$mail->isHTML(true);
$mail->Subject = '【重要】月度報告 - ' . date('Y-m');
$mail->Priority = 1; // 1=高, 3=普通, 5=低
$mail->Body = '
<html><body>
<img src="cid:banner_cid" alt="Banner" style="max-width:100%">
<h1>月度報告</h1>
<p>親愛的用戶,附件為本月報告,請查收。</p>
<img src="cid:logo_cid" alt="Logo">
</body></html>
';
$mail->AltBody = '月度報告已寄出,請查收附件。';
// 除錯模式 (開發時啟用)
// $mail->SMTPDebug = 2; // 0=關閉, 1=客戶端, 2=伺服器, 3=連線, 4=低階
$mail->send();
echo '郵件發送成功!';
?>
| 錯誤訊息 | 原因 | 解決 |
|---|---|---|
| SMTP connect() failed | 主機/埠口/加密錯誤、防火牆阻擋 | 檢查設定、telnet 測試連線、開放防火牆 |
| 535 Authentication failed | 帳號/密碼錯誤 | Gmail 用應用程式密碼、163 用授權碼、企業郵箱確認 AD 密碼 |
| 550 Mailbox unavailable | 收件信箱不存在/滿/被封 | 確認收件地址正確 |
| 554 Transaction failed / Spam | 被判定垃圾郵件 | 設定 SPF/DKIM/DMARC、避免垃圾詞、用專業郵件服務 |
| SSL routines:ssl3_get_server_certificate:certificate verify failed | SSL 憑證驗證失敗 | $mail->SMTPOptions = ['ssl'=>['verify_peer'=>false,'verify_peer_name'=>false,'allow_self_signed'=>true]]; (僅測試用) |
| Could not instantiate mail function | 未呼叫 isSMTP() 卻用 SMTP 設定 | 確認 $mail->isSMTP() 在設定之前 |
<?php
// test_mail.php - 放在專案根目錄,瀏覽器或 CLI 執行
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = 'test@gmail.com';
$mail->Password = 'app_password';
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
$mail->CharSet = 'UTF-8';
$mail->setFrom('test@gmail.com', '測試系統');
$mail->addAddress('test@gmail.com'); // 寄給自己
$mail->Subject = 'PHPMailer 測試 ' . date('H:i:s');
$mail->Body = '<h1>測試成功!</h1><p>時間: ' . date('Y-m-d H:i:s') . '</p>';
try {
$mail->send();
echo "✅ 發送成功\n";
} catch (Exception $e) {
echo "❌ 發送失敗: {$mail->ErrorInfo}\n";
if ($mail->SMTPDebug) echo $mail->getSMTPInstance()->getError();
}
?>
結論:mail() 適合有完整 MTA 基礎設施的內部系統;對外發送、現代雲端環境、需 HTML/附件/認證的場景,**PHPMailer 是標準解法**,穩定、功能全、社群支援好。