重新安装站点并配置nullmailer作为MTA以后,发现我的站点在外发email时,和以前不太一样了。现在,收信方查看email时,会发现正文多了些类似乱码的MIME信息,而且发件人也没有正确填写:

解决尝试一
一开始,怀疑是我装的WP Mail SMTP Plugin插件复写了mail()函数的缘故。
但卸载这插件后症状依旧,所以不是这个引起的。
解决尝试二
外网上有提到如何移除PHPMailer的header,参见:
javascript – Removing the PHP Mailer sent header – Stack Overflow
文中提到需在php代码中添加 $instance->SMTPDebug = false;
但我尝试了,并无效果。
后来我查证,WordPress代码默认就没有启用也无意使用SMTPDebug变量,并且在新版本的WordPress中,内嵌的PHPMailer也不是直接调用的,而是在pluggable.php中把PHPMailer派生成wp_PHPMailer类并封装成wp_mail()函数包来调用的。WordPress所有和收发邮件有关的操作,其实都仅仅是通过wp_mail()来执行的。
所以其实无需也不应该去动底层的PHPMailer,以及PHPMailer等变量。
解决尝试三
到这一步,我想有没有可能是php的配置出了问题呢?
我验证了php.ini配置文件中sendmail_path等变量,和出现异常之前是一样的。
然后也验证了nullmailer和mailutils等主机端MTA程序的配置文件和日志,也没有发现异常。
并且不能否认的是,email都是可以正常被发出和接收的,只是显示出了点问题。
所以基本可以断定配置的问题不大,整个链路流程应该是正确的。
于是我怀疑是email发出时,编码和换行识别的问题。这是一种常见错误,这种错误是可能导致MIME被当作正文,并且使发件人信息错位。
我找了一下,发现在php.ini配置中有一个以前没注意过的选项:mail.mixed_lf_and_crlf
默认情况下,它是Off的。
从说明来看,它用于显式地混杂lf和crlf这些回车换行符,使得邮件文本的兼容性更好,能被一些和RFC 2822 不完全兼容的MTA程序所识别(Use mixed LF and CRLF line separators to keep compatibility with some RFC 2822 non conformant MTA)。
我把它改成On。再测试,问题就解决了。
至此,邮件正文不再出现MIME信息了,发件人也能正常显示了。
2025.05.27更新
最近发现这么改,仍然有些邮件是无法避免这个问题的(header仍然被识别成正文,发送HTML格式的邮件更甚)。
再次深入挖掘这个问题,发现还是跟字符编码/换行格式有关!最终我找到这个github issue,它基本坐实了这个问题和PHP >= 8.2(8.0?)版本有关。
详细展开一下:虽然这个issue本身提的是新版本PHPMailer和大于8.2版本的PHP在使用nullmailer时会出现这个问题,但其实,这个问题是PHP >= 8.2 时新增的修改,或者说是SMTP mandates RFC-compliant后发生的演进。PHPMailer挺无辜的,它只是严格遵循了这个新的变化。
PHP >= 8.2后,应SMTP新规的要求,本身自带的mail()函数或者说默认处理邮件时都会强制使用CRLF断句。并且,如果你不使用插件去截获PHP的这个mail()函数的话,PHP自带的mail()函数最终会调用系统环境的MTA程序来发送邮件,于是,就出现了我们遇到的这个兼容性问题。
虽说PHPMailer本身就支持指定多种发送程序,并且这个github issue指出,在强制指定使用系统环境的MTA程序(‘’smtp’ === $this->Mailer’触发)时、或指定使用PHP本身自带的mail()函数(‘’mail’ === $this->Mailer’触发)并且PHP版本大于8.0时,可以手动替换这个CRLF的ending为PHP_EOL,来达到目的:

但我们先要捋一遍:既然PHPMailer支持指定多种发送程序,那对于WordPress的流程,是否一定会进入到static::setLE(self::CRLF)这里?
因为如前述,WordPress会首先把PHPMailer派生成wp_PHPMailer类,然后才会使用,所以我们需要确认wp_PHPMailer不会私自动手脚。而经过查证,派生以后wp_PHPMailer就是强制PHPMailer只能使用PHP本身的mail()函数来发送email的(强制调用$phpmailer->isMail()来指定),并且我们的PHP的版本的确大于8.0,所以毫无疑问,这的确会触发这个判定并进入上述语句块内。
也就是说,我们确实可以如github issue所述,修改这条语句来达到目的(我事后也验证过了,此举的确完全修复了这个问题),至此问题解决。但不得不说,这个修改只能临时生效,WordPress更新以后就会被覆盖。
故我们最正确的办法,就是只能安装WordPress的SMTP/email插件,让这些插件截获PHP的mail()函数并让这些插件在PHP层面就把邮件发送走,然后我们配置这些插件和email服务器对接好即可。因为原生mail()函数会强制走系统底层的MTA程序从而触发这个兼容性问题,截获后就不再走系统底层MTA程序了;而无论是PHP >= 8.2还是PHPMailer还是这些插件,都已经很好适应了SMTP新规,而且我们也没有经过系统命令行环境,故不会触发系统命令行环境换行兼容性带来的问题。
只是,照这情况,PHP 8.2之后,我们便再也无法很好使用nullmailer这个系统MTA了。默哀三分钟。
总结
“当排除了所有的可能,那剩下的那个,哪怕再不可能,也只能是真相。”
关于如何给WordPress配置MTA的前情背景,请参考: