Hugh's Blog

PHPMailer 在 SSL 配置失败时的解决方法

今天在使用 PHPMailer 发邮件时遇到一个问题,显示错误是 Could not connect to SMTP host,由于之前测试成功的服务器在香港,网站上线之后转到大陆服务器了,而且 smtp 的服务器也是在香港,所以以为是服务器的原因,但是在服务器上 ping smtp 服务器是有反应的,所以应该并不是不能连接 smtp 服务器的原因。

在网上找了下类似的问题,有人提到是因为没有开启 openssl 的原因,想想很有可能,因为正好使用的是 465 ssl 端口,但查看了下,发现 openssl 已经开启了,再对比下香港服务器的 PHP 配置信息,发现大陆服务器的设置中多了两个参数,分别是 openssl.cafile 和 openssl.capath,而这两个值是空的,再对比下版本,香港服务器的 PHP 版本是 5.5.x,而大陆的是 5.6.x。

最终在 stackoverflow 找到了解决方法,有人提到了 5.6 以上的版本中 openssl 证书验证失败时 PHPMailer 需要对 smtp 设置添加一些额外的参数,需要添加下面的一段代码:

// PHPMailer Version: 5.2.16
$mail->SMTPOptions = [
	'ssl' => [
		'verify_peer' => false,
		'verify_peer_name' => false,
		'allow_self_signed' => true
	]
];

添加了之后,确实可以发送邮件了。

在 PHPMailer 官方 wiki 中有提到,openssl 失败时会返回以下信息,不过却没找到在哪里可以看到这段日志。

Warning: stream_socket_enable_crypto(): SSL operation failed with code 1.
OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed`

需要注意的是,上面的解决办法也是临时的,并不建议。

2017-04-27

最近服务器 PHP 的版本升级到 5.6 了,个别网站的邮件功能受到了影响,那些把 PHPMailer 作为第三方引入的网站还好处理,但是有相当一部分网站使用的是 WordPress,其中 SMTP 设置使用的是这个插件:WP Mail SMTP,该插件很好用,代码也很简洁。本来还以为要移除插件来重新修改邮件功能,所幸插件作者提供了 PHPMailer 的额外参数设置钩子:wp_mail_smtp_custom_options,在 functions.php 中加入下面代码就行。

add_filter('wp_mail_smtp_custom_options', 'my_wp_mail_smtp_custom_options');
function my_wp_mail_smtp_custom_options($phpmailer)
{
    $phpmailer->SMTPOptions = array(
        'ssl' => array(
            'verify_peer' => false,
            'verify_peer_name' => false,
            'allow_self_signed' => true
        )
    );
    return $phpmailer;
}

2017-07-21

今天又遇到发邮件失败的情况了 :(

确定了是服务器的问题,PHP 版本 5.6,但是使用上面的方法并没有作用,下面是一些 debug 信息:

...
220-We do not authorize the use of this system to transport unsolicited,
220 and/or bulk e-mail.
...
SMTP Error: Could not authenticate
...

看起来不能验证账号,而 SMTP 服务器是能够连接的,也显示了密码验证的过程,但就是验证失败,觉得很奇怪,Google 找到说是 cPanel/WHM 的原因,试了下,竟然成功了,原因是 cPanel 开启了 Restrict outgoing SMTP to root, exim, and mailman (FKA SMTP Tweak),关闭了就好。

登录到 cPanel 后台,定位到 Home » Server Configuration » Tweak Settings,修改以下几个配置项的值。

Restrict outgoing SMTP to root, exim, and mailman (FKA SMTP Tweak) - Off

Prevent “nobody” from sending mail - On

Allow users to relay mail if they use an IP address through which someone has validated an IMAP or POP3 login within the last hour (Pop-before-SMTP) - On

Add X-PopBeforeSMTP header for mail sent via POP-before-SMTP - On

我只修改了第一个就好了,其他的没动。

查找了下,这个配置是由于垃圾邮件发送一般会通过远程连接邮件服务器,而 cPanel 默认会开启 SMTP Restrictions 来防止用户这样做,更多信息可以看 这里

额外说下,如果出现 5.6 版本 SSL 验证失败的问题,可以试下 TLS 验证,今天使用 TLS 并没出现什么问题。

还有做个记录吧,如果 Gmail SMTP 不能使用的话,可以试下修改账号的两个设置:google less secure appsgoogle unlock captcha,Google 搜索这两个关键词,一般来说,设置了之后 SMTP 就能使用了。

20171012

今天在重启服务器的时候,邮箱又发送不了了,报如下错误:

php_network_getaddresses: getaddrinfo failed: Name or service not known

看起来是服务器无法访问外网导致的,在服务器上使用 nslookup smtp.gmail.com 查看下是否能访问,不能的话修改下 DNS 应该就行了。


参考

PHPMailer: SMTP Error: Could not connect to SMTP host

PHP 5.6 certificate verification failure

OpenSSL changes in PHP 5.6.x

TLS Peer Verification w/PHP 5.6 and WordPress SMTP Email plugin

cPanel Restrict outgoing SMTP Error