邮件发不出去主因是MAIL_MAILER未设、to()未调用或密码非应用专用密码;需运行php artisan tinker检查config('mail'),确认default值、SMTP凭据及from地址正确,并清缓存。

邮件发不出去,八成不是代码写错了,而是 MAIL_MAILER 没设对、MAIL_PASSWORD 用的是登录密码、或者 to() 根本没调用——这些地方一错,Laravel 就会静默失败或直接报 Symfony\Component\Mailer\Exception\TransportException: No recipients specified。
怎么确认邮件配置真生效了
别只盯着 .env 文件改完就跑,Laravel 实际加载的配置可能被缓存或覆盖。最直接的办法是进 php artisan tinker,执行:
config('mail')
看输出里 default 和 mailers.smtp 下的值是否和你预期一致。重点核对:
-
default必须等于env('MAIL_MAILER')的值(比如smtp),不能是空或拼写错误 -
mailers.smtp.username和password要能从.env正确读出,注意别漏掉引号或空格 -
from.address必须是合法邮箱格式,Gmail/qq 等服务商对发件人校验很严
开发阶段建议先把 MAIL_MAILER=log,发一封测试邮件,然后去 storage/logs/laravel.log 里搜 [mail],能看到完整原始内容,比配错 SMTP 干等超时快得多。
SMTP 配置最容易踩的三个坑
Gmail、QQ、163 这类常用 SMTP 服务,表面参数差不多,实际细节差很多:
- Gmail 必须用 应用专用密码,不是账户密码;且要先开两步验证才能生成;
MAIL_ENCRYPTION设tls时端口必须是587,设ssl就得换465 - QQ 邮箱的
MAIL_PASSWORD是“POP3/SMTP 服务”里生成的授权码,不是 QQ 登录密码;MAIL_HOST必须是smtp.qq.com,写成smtp.qq.com.(末尾多点)都会连不上 - 163 邮箱默认端口是
25,但很多云服务器会封25端口,得切到465+ssl,同时在config/mail.php的smtp配置里加'stream' => ['ssl' => ['verify_peer' => false]]绕过证书校验(仅限测试)
改完 .env 一定要运行 php artisan config:clear,否则 Laravel 读的还是旧缓存。
发送前必须调 to(),而且只能传字符串或数组
Mailable 类不是 new 完就发,它是个构建器。漏掉 to() 会直接抛异常,不是静默丢弃:
- 正确写法:
Mail::to('user@example.com')->send(new WelcomeEmail())或Mail::to(['a@x.com', 'b@x.com'])->send(new WelcomeEmail()) - 错误写法:
Mail::send(new WelcomeEmail())(Mail::send()已废弃)、Mail::to($user)->send(...)($user是 Eloquent 模型,哪怕有email属性也不行) - 批量发送别写循环:
foreach ($users as $u) { Mail::to($u->email)->send(...); }—— 改用Mail::to($users)->send(...),Laravel 会自动提取每个模型的email属性
如果走队列,to() 必须在 dispatch() 前完成,写在 job handle 方法里会找不到收件人上下文。
附件中文名和流式文件怎么处理
用 attach() 传相对路径会失败,因为队列 job 或不同环境工作目录不一致:
- 附件路径必须是绝对路径,比如
storage_path('app/reports/report.pdf') - 中文文件名不能靠
attach()自动识别,得用attachData():$this->attachData(file_get_contents($path), '订单汇总-20260512.pdf') - 大文件别直接
file_get_contents()加载进内存,改用attachFromStorageDisk()或自定义 stream resource
另外,Mail::raw() 只适合极简测试,没法用 assertSentMail() 写测试,也没法复用模板或主题逻辑,上线前务必迁移到 Mailable 类。
真正卡住人的往往不是语法,而是 MAIL_MAILER 值被 config cache 锁死、to() 调用时机不对、或者 Gmail/QQ 的凭据类型搞混——这些地方一错,日志里可能连错误都看不到。


















