因业务需要,今天做了一个简单的邮件发送功能,通过 PHPMailer 来实现。本地测试正常,部署到阿里云 ECS 后,报错:SMTP connect() failed ,本文简单记录下解决方案。

使用 SMTP 服务发送邮件,常用的端口有 25465,其中后者是 SSL 端口。一般来说,各云服务商都会封禁 25 端口,包括阿里云也不例外。阿里云可以在控制台申请解封 25 端口,但据网上反馈的信息,申请后基本没人处理,另外也完全没必要用 25 端口。因此,下文就仅针对 465 端口的情况进行说明。

代码部署到服务器后,调用发送邮件方法直接报错:SMTP connect() failed 。因为使用的阿里云,因此我们首先想到的就是去安全组添加白名单。在安全组中 出方向 中新增一条记录,端口对应 465/465 。添加完成后,我们可以在服务器上验证下。

[root@blog.phpha.com]# telnet smtp.mxhichina.com 465
Trying 140.205.94.10...
Connected to smtp.aliyun.com.
Escape character is '^]'.

我们使用的是阿里云企业邮箱,以上结果代表 465 端口已经开通权限。

再次执行发送邮件,依然报错:SMTP connect() failed 。这个错误相对来说比较粗糙,因此我们可以打开调试模式来查看发送过程中详细的日志。

只需要设置下 PHPMailer 的配置参数即可:$mail->SMTPDebug = 4 ,再次执行可以看到如下详细的错误:

Connection: opening to ssl://smtp.mxhichina.com:465, timeout=300, options=array()
Connection failed. Error #2: stream_socket_client(): SSL operation failed with code 1.
OpenSSL Error messages:
error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed [/xxx/phpmailer/src/SMTP.php line 349]
Connection failed. Error #2: stream_socket_client(): Failed to enable crypto [/xxx/phpmailer/src/SMTP.php line 349]
Connection failed. Error #2: stream_socket_client(): unable to connect to ssl://smtp.mxhichina.com:465 (Unknown error) [/xxx/phpmailer/src/SMTP.php line 349]
SMTP ERROR: Failed to connect to server:  (0)
SMTP connect() failed. https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting

重点是这个报错:tls_process_server_certificate:certificate verify failed ,看起来是跟证书相关。经过相关的搜索得知: PHP5.6 及以后的版本中如使用 stream_socket_client() / fsockopen() / file_get_content() 等函数获取 HTTPS 站点的信息时,OPENSSL 会验证对方站点的 SSL 证书颁发机构是否可信,如果未配置 OPENSSL 根证书就会造成无法验证对方网站 SSL 证书是否可信,就无法获取到内容。因此,下面我们来配置下 OPENSSL 根证书。

# 下载最新根证书
[root@blog.phpha.com]# wget https://curl.haxx.se/ca/cacert.pem
# 移动到指定路径
[root@blog.phpha.com]# mv cacert.pem /usr/local/nginx/conf/ssl/cacert.pem
# 编辑 php.ini
[root@blog.phpha.com]# vim /usr/local/php/etc/php.ini
# 增加以下配置
openssl.cafile=/usr/local/nginx/conf/ssl/cacert.pem

之后重启 NginxApache ,再次调用邮件发送的方法,即可正常发送。

标签:PHP