Symfony Mailer发邮件需严格配置MAILER_DSN(如Gmail必须含?auth_mode=login),模板路径须以templates/开头且用context()传参,禁用spool和异步队列可避免静默失败,多传输通过X-Transport头灵活切换。

MAILER_DSN 配错就静默失败,不是报错而是根本没发
Symfony Mailer 不会因为你 DSN 少个 ?auth_mode=login 就抛异常,它直接 fallback 到 null://——发件人收不到任何提示,收件人收不到任何邮件。Gmail 必须带 auth_mode=login,否则即使端口是 465、加密写 ssl,也连不上。
常见错误组合:
-
smtps://user:pass@smtp.gmail.com:465—— 错,smtps://已废弃,必须用smtp://+encryption=ssl -
smtp://user:pass@smtp.gmail.com:587—— 错,缺&auth_mode=login,Gmail 拒绝认证 -
smtp://user:pass@smtp.gmail.com:587?encryption=tls&auth_mode=login&username=user—— 错,username和password是 DSN 用户信息的一部分,重复写会导致解析失败
验证是否真配上了:运行 bin/console debug:config mailer,看输出里 transports 下的 DSN 是否和你 .env 里写的完全一致,而不是显示 null://。
TemplatedEmail 模板路径和传参不按规矩来,渲染就报 404
用 TemplatedEmail 发 HTML 邮件时,htmlTemplate() 只接受以 templates/ 开头的相对路径,且不能带扩展名前缀(如 templates/email/welcome.html.twig,调用时只写 'email/welcome.html.twig')。
变量传递也容易错:
- 必须用
->context(['user' => $user]),不是->with()(那是旧 SwiftMailer 的写法) -
context()传进去的数组,模板里直接用{{ user.name }},没有app或global前缀 - 如果开了
inline_css: true却没装symfony/css-selector和tijsverkoyen/css-to-inline-styles,Mailer 会自动降级为纯文本,不警告也不报错
开发时邮件“发了但没收到”,大概率是异步或 spool 拦住了
Symfony 6.2+ 已彻底移除 spool,但如果你的 config/packages/mailer.yaml 还留着 spool: { type: memory },Mailer 会静默忽略并用 null:// 替代——等于没配置传输。
异步更隐蔽:只要项目启用了 Messenger(比如装了 symfony/messenger-bundle),默认就会把邮件塞进 messenger.transport.async。你没跑 bin/console messenger:consume,邮件就卡在数据库里不动。
开发阶段最稳的做法:
- 在
config/packages/mailer.yaml显式写死:transport: 'smtp'(别留空、别写null、别依赖环境变量 fallback) - 删掉所有
spool:配置项 - 确认
bin/console debug:router里没有意外注册messenger相关路由干扰
多 SMTP 场景下切传输,靠的是 X-Transport 头不是 new Mailer()
不要为每个通道 new 一个 Mailer 实例——那会绕过 Symfony 的服务容器和事件系统,也浪费连接资源。正确做法是配多个 transport,再用头切换:
先在 config/packages/mailer.yaml 定义:
framework:
mailer:
transports:
default: '%env(MAILER_DSN)%'
sendgrid: '%env(MAILER_DSN_SENDGRID)%'
然后在发重要邮件时加头:
$email->getHeaders()->addTextHeader('X-Transport', 'sendgrid');
这个头不会出现在最终发出的邮件里,Mailer 内部识别后自动换 transport,且仍走统一的 MessageEvent::MESSAGE_SENT 等事件流。漏掉这行,哪怕 DSN 配了十个,也永远只走 default。


















