php的mail()函数常失败是因为它仅将邮件交给MTA而不保证投递,且不报错;应改用PHPmailer等库并配置SMTP、app Password及DNS记录。

PHP 用 mail() 发邮件为什么经常失败
绝大多数 PHP 邮件发送失败,不是代码写错了,而是 mail() 函数根本没连上本地或远程 MTA(比如 sendmail、Postfix)。它不报错,但返回 true 假成功——因为只负责把邮件“扔给”系统命令,后续投递完全脱手。
常见现象:mail() 返回 true,但收件人永远收不到;测试时发到 Gmail 显示 “this message was not encrypted”,其实是压根没进队列。
- linux 下需确认
/usr/sbin/sendmail存在且可执行,且 PHP 的sendmail_path配置正确(phpinfo()查) - windows 下默认不可用,除非手动配置 Mercury Mail 或类似服务,实际几乎没人这么干
- 多数共享主机禁用
mail(),或强制改写 From 头,导致 SPF 检查失败被拒收
用 PHPMailer 发 Gmail 邮件的最小可行配置
Gmail 要求 OAuth2 或开启「低安全性应用访问权限」(已逐步废弃),现在必须用 App Password(两步验证开启后生成)。
示例中关键点不是“能发”,而是避免踩坑:
立即学习“PHP免费学习笔记(深入)”;
- SMTP 主机必须是
smtp.gmail.com,端口选587(TLS)或465(ssl),别混用 -
setFrom()的邮箱必须和登录账号一致,否则 Gmail 直接拒信(即使 SMTP 认证通过) - 密码字段填的是 16 位「App Password」,不是你的 Gmail 登录密码
- 务必调用
$mail->isSMTP(),否则 PHPMailer 默认走mail(),又绕回第一段的问题
use PHPMailerPHPMailerPHPMailer; $mail = new PHPMailer(); $mail->isSMTP(); $mail->Host = 'smtp.gmail.com'; $mail->Port = 587; $mail->SMTPAuth = true; $mail->Username = 'your@gmail.com'; $mail->Password = 'abcd efgh ijkl mnop'; // App Password,带空格 $mail->setFrom('your@gmail.com', 'Your Name'); $mail->addAddress('to@example.com'); $mail->Subject = 'Test'; $mail->Body = 'Hello'; $mail->send();
为什么不要自己拼接 MIME 邮件头
手动用 mail() + 自定义 headers 参数发带附件或 html 的邮件,等于主动跳进编码、换行、边界符、Content-Transfer-Encoding 的深坑。
典型错误包括:
- 中文主题没用
mb_encode_mimeheader(),收件箱显示乱码或问号 - HTML 正文没设
Content-Type: text/html; charset=UTF-8,outlook 当纯文本渲染 - 附件路径含空格或中文,
Content-Disposition里没做 RFC 2231 编码,附件名损坏 - 换行符用
n而非rn,部分 MTAs 直接丢弃整封邮件
这些逻辑 PHPMailer、symfony Mailer 等库早已封装妥当,重复造轮子只会让调试时间远超开发时间。
生产环境必须检查的三件事
上线前不核对这三项,发信功能大概率在某次更新后突然失效:
- 服务器出站 587/465 端口是否被云厂商(如阿里云、AWS)默认屏蔽?需工单申请解封
- Gmail 的 App Password 是否过期(不会通知),或账号启用了新设备登录导致旧密码失效
- 是否漏了设置
DKIM和SPFDNS 记录?否则大量邮件进垃圾箱,且无法通过 Gmail 的Authentication-Results头排查
真正麻烦的从来不是“怎么发”,而是“为什么收不到”——而后者 90% 以上跟 PHP 代码无关。