Swiftmailer问题:CLI可发送邮件,浏览器端发送失败
这种情况我之前碰过好多次,结合Symfony和SwiftMailer的运行机制,大概率是下面几个原因,你可以挨个排查:
环境配置不一致
Symfony默认区分开发(dev)、生产(prod)等环境,命令行和浏览器请求可能加载的是不同的配置。比如:- 检查
config/packages/dev/swiftmailer.yaml里是不是设置了delivery_address把所有邮件转发到测试邮箱,或者disable_delivery: true直接禁用了发送; - 对比
.env和.env.local文件,确认命令行和Web环境用的MAILER_URL(或MAILER_DSN)是不是一致,有没有Web环境用了错误的邮箱服务器配置。
- 检查
Spool目录权限问题
如果你的SwiftMailer配置了文件型spool(spool: { type: file }),邮件会先存在var/spool目录下再发送。命令行运行的用户(比如你终端的登录用户)和Web服务器的用户(比如www-data、apache)权限不同,可能导致Web用户没有读写var/spool的权限,邮件根本存不进去,自然发不出去。
解决方法:给var/spool设置正确的权限,比如执行chown -R www-data:www-data var/spool,或者给目录设置775权限(注意生产环境要兼顾安全)。异步邮件未触发发送
如果用了内存型spool(spool: { type: memory }),Web请求结束后PHP进程就会销毁,内存里的邮件队列也跟着没了;如果是文件spool,命令行下你可能手动执行了php bin/console swiftmailer:spool:send来发送队列,但Web环境下没人触发这个操作。
解决思路:要么在请求结束时手动触发spool发送(比如在控制器里调用$this->get('swiftmailer.mailer')->getTransport()->stop()),要么改用Messenger组件来管理异步邮件队列,确保有消费者进程在处理队列。Web环境PHP配置限制
Web服务器的PHP配置可能和命令行的不一样,导致邮件发送失败:- 检查
max_execution_time是不是太短,邮件发送还没完成请求就超时了; - 看
open_basedir有没有限制SwiftMailer访问必要的文件(比如SSL证书文件); - 确认
disable_functions里没有禁用mail()、fsockopen这些SwiftMailer依赖的函数。
你可以分别在命令行执行php -i、在浏览器里输出phpinfo(),对比两者的配置差异。
- 检查
异常被静默捕获
浏览器请求的代码里,可能有try-catch块吞了邮件发送的异常,而且没有记录日志,导致你看不到错误。比如控制器里的代码是这样的:try { $mailer->send($message); } catch (\Exception $e) { // 什么都没做,异常被吞了 }而命令行的代码没有这个捕获逻辑,所以能正常发送(或者报错能看到)。检查你的代码,确保异常被记录到日志里,比如用Symfony的Logger组件:
try { $mailer->send($message); } catch (\Exception $e) { $this->logger->error('邮件发送失败', ['exception' => $e]); throw $e; // 或者根据业务逻辑处理 }缓存未更新
如果你修改了SwiftMailer的配置,但只清理了命令行环境的缓存,Web环境的缓存还保留着旧配置。执行对应环境的缓存清理命令,比如生产环境:php bin/console cache:clear --env=prod
内容的提问来源于stack exchange,提问作者Сергей Шевченко

