背景与问题来源
SMTP数据阶段的点号约定
在SMTP协议中,数据部分以一个单独的点行结束。任何以点号开头的文本行都需要被“点前缀”处理,以确保传输的正确性。
如果发送方在发送邮件正文时没有进行正确的点前缀处理,包含以点开头的行的邮件正文就可能在接收端被误认为数据结束,导致发送失败或内容丢失。
PHP mail() 与 sendmail 的工作流程
在 Linux/Unix 系统,PHP 的 mail() 实际上把邮件送给系统的发送代理(如 sendmail、postfix、exim 等)。sendmail 作为 MTA 将处理邮件内容的 DATA 阶段,若正文包含未点前缀的点行,传输可能失败。
因此,正确的做法通常是在消息体中对以点开头的行进行点前缀,以让 MTA 正常通过 SMTP DATA 阶段。这一步是与 sendmail 共同协作的关键。
问题表现与排错要点
错误表现与可疑迹象
常见表现包括:发送后没有收到服务器确认、或者收件人端显示“data rejected”/“SMTP 554”类型错误,以及部分邮件被截断或包含“.”开头的空行。
在排错时,可以通过在本地临时加大换行符的稳定性来测试:用 CRLF(\r\n)替换为默认的 LF,观察是否仍有问题。
诊断步骤与测试方法
使用 telnet / openssl s_client 手工连接到 SMTP 端口,逐步发送 HELO、MAIL FROM、RCPT TO、DATA,并在 DATA 块前后观察服务器对点前缀的要求,能快速定位是否存在点前缀处理缺失的问题。这类诊断可以直接验证点前缀的必要性。

解决方案概览
手动点点前缀实现思路
核心思路是在发送前对邮件正文逐行检查,把以点开头的行前再加一个点,确保发送到服务器时不会触发数据结束的信号。
实现时要确保行结束符统一为 \r\n,并且处理不同系统的换行差异,避免多余的空行干扰。
具体实现:代码示例与集成
自定义 dot-stuff 函数示例
上述实现突出显示了点前缀处理的核心逻辑,并在发送前确保所有行都符合 SMTP 传输要求。
与 sendmail 的兼容性与实际调用
在大多数 Linux/Unix 环境中,PHP 的 mail() 调用最终走向 sendmail,因此点前缀处理在发送数据阶段仍然生效。若服务器配置了不同的 MTA,点前缀规则同样适用。
要注意:某些托管环境对 mail() 的参数格式有严格约束,请确保 headers 与 body 的 CRLF 规范一致,避免额外的空行造成传输异常。
进阶选项:使用第三方邮件库的对比与优势
PHPMailer/SwiftMailer 自动处理点前缀
优秀的邮件库在组装 MIME 与 DATA 时,会对正文进行正确处理,自动实现点前缀或通过底层 SMTP 客户端正确转义,从而降低手动实现的风险。
当业务对邮件正文中包含大量以点开头的行时,使用成熟的邮件库可以提升可靠性与可维护性。


