PHP mail()失败主因是PHP进程用户权限不足:/usr/sbin/sendmail需可执行,sendmail_path路径须正确且可访问,系统临时目录(如/tmp)需可写,SELinux/AppArmor策略可能拦截,须用实际运行用户模拟测试验证。

PHP 修改文件或目录权限本身不会直接影响 mail() 函数能否发邮件,但权限设置不当可能间接导致邮件发送失败——关键在于 PHP 进程能否访问邮件相关资源(如 sendmail 二进制、临时目录、配置文件)。
PHP mail() 发送失败时,哪些权限最常出问题
排查重点不是 PHP 脚本自身的读写权限,而是 PHP 运行用户(如 www-data、apache 或 nginx)是否具备调用系统邮件代理的权限:
-
/usr/sbin/sendmail(或/usr/bin/sendmail)必须对 PHP 进程用户可执行;若被设为700且属主非 PHP 用户,则调用失败 - PHP 的
sendmail_path配置指向的路径必须存在且可执行;常见错误是路径写错,或符号链接断裂(ls -l /usr/sbin/sendmail看是否指向真实二进制) -
sys_temp_dir(或系统默认临时目录如/tmp)需对 PHP 用户可写;否则邮件头解析、附件临时存储会失败,报错类似failed to open stream: Permission denied - 如果使用
ini_set('sendmail_path', ...)动态覆盖路径,该字符串里的命令及其依赖(如-t参数需要的 stdin 处理)也受权限约束
如何快速验证 sendmail 权限是否 OK
别只看 mail() 返回 true,它只表示“提交成功”,不保证实际投递。直接模拟 PHP 调用方式测试:
sudo -u www-data /usr/sbin/sendmail -t <<'EOF' To: test@example.com Subject: CLI Test This is a test. EOF
如果报 Permission denied 或 No such file or directory,说明权限或路径有问题。注意:sudo -u 必须用 PHP 实际运行的用户,不是 root 或当前登录用户。
立即学习“PHP免费学习笔记(深入)”;
- 若提示
command not found:检查sendmail_path配置值是否与which sendmail输出一致 - 若提示
cannot execute binary file:可能是架构不匹配(如 ARM 服务器装了 x86 sendmail),或 SELinux/AppArmor 拦截(见下一条) - 若静默无输出且收不到邮件:检查
/var/log/mail.log或journalctl -u postfix,确认 MTA 是否收到请求
SELinux / AppArmor 也会“假装是权限问题”
在 CentOS/RHEL(SELinux)或 Ubuntu(AppArmor)上,即使文件权限全开,安全模块仍可能阻止 Apache/Nginx 进程执行 sendmail。现象是:mail() 返回 false,日志里没有 sendmail 调用记录,audit.log 中出现 avc: denied。
- 临时验证:运行
setenforce 0(SELinux)或aa-disable /etc/apparmor.d/usr.sbin.apache2(Ubuntu),再试mail();若恢复则确认是策略拦截 - 永久修复:不要关 SELinux,而是用
ausearch -m avc -ts recent | audit2why分析,然后用audit2allow生成新规则 - 常见布尔值:SELinux 下检查
httpd_can_sendmail是否为on(getsebool httpd_can_sendmail)
真正卡住人的往往不是 sendmail 权限本身,而是 PHP 进程用户和 MTA 用户之间的信任链断在哪一环——比如 Postfix 配置了 smtpd_relay_restrictions 拒绝本地未认证提交,或 Docker 容器里没挂载 /usr/sbin/sendmail。先确认 PHP 能否以对应用户身份手动跑通 sendmail,再查代码逻辑,比盲目改 chmod 777 有效得多。











