标签: 阿里云

  • 阿里云虚机 WP 发信失败?证书验证错误 SMTP Failed to connect

    给 WordPress 添加了评论回复邮件提醒功能,但是测试时无法收到邮件。查看 Web 服务器错误日志,看到 WP_Error Object: wp_mail_failed: SMTP Failed to connect to server 等报错信息。SMTP 连接信息和用户名与密码都是正确的,仅凭这一点报错信息不能确定 SMTP 无法连接到服务器的具体原因,所以单独写一个测试程序 test_mail.php 放到 WordPress 程序根目录下,打印详细的调试信息。test_mail.php 代码如下:

    <?php
    error_reporting(E_ALL);
    
    echo "importing\n";
    require_once 'wp-includes/PHPMailer/PHPMailer.php';
    require_once 'wp-includes/PHPMailer/SMTP.php';
    
    echo "creating mailer\n";
    $mail = new PHPMailer\PHPMailer\PHPMailer();
    
    echo "setting\n";
    $mail->CharSet="UTF-8";           //设定邮件编码,默认ISO-8859-1,如果发中文此项必须设置为 UTF-8
    $mail->isSMTP();                  //设定使用SMTP服务
    $mail->SMTPAuth = true;           //启用SMTP验证功能
    $mail->SMTPSecure = "ssl";        //启用SSL
    $mail->SMTPDebug = true;
    $mail->Host = "smtpdm.aliyun.com";                                //SMTP服务器
    $mail->Port = 465;                                                //SMTP服务器的端口号
    $mail->Username = "USERNAME";                                     //SMTP服务器用户名
    $mail->Password = "PASSWORD";                                     //SMTP服务器密码
    $mail->setFrom('notifications@www.eulerkey.cn', 'EulerKey');      //设置发件人地址和名称
    $mail->addReplyTo("notifications@www.eulerkey.cn","EulerKey");    //设置邮件回复人地址和名称
    $mail->Subject = '邮件标题';                                      //设置邮件标题
    $mail->AltBody = "为了查看该邮件,请切换到支持HTML的邮件客户端";     //可选项,向下兼容考虑
    $mail->msgHTML('<html>hello</html>');                            //设置邮件内容
    $mail->addAddress('admin@eulerkey.cn', 'EulerKey Admin');        //设置收件人地址和名称
    
    echo "sending\n";
    if(!$mail->Send()) {
        echo "发送失败:" . $mail->ErrorInfo;
    } else {
        echo "恭喜,邮件发送成功!";
    }Code language: PHP (php)

    运行这个 test_mail.php,得到了下面的详细信息:

    2025-12-18 10:56:38 Connection: opening to ssl://smtpdm.aliyun.com:465, timeout=300, options=array()
    2025-12-18 10:56:38 Connection: stream_socket_client not available, falling back to fsockopen
    2025-12-18 10:56:38 Connection failed. Error #2: fsockopen(): SSL operation failed with code 1. OpenSSL Error messages:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed [/usr/home/htdocs/wp-includes/PHPMailer/SMTP.php line 439]
    2025-12-18 10:56:38 Connection failed. Error #2: fsockopen(): Failed to enable crypto [/usr/home/htdocs/wp-includes/PHPMailer/SMTP.php line 439]
    2025-12-18 10:56:38 Connection failed. Error #2: fsockopen(): Unable to connect to ssl://smtpdm.aliyun.com:465 (Unknown error) [/usr/home/htdocs/wp-includes/PHPMailer/SMTP.php line 439]
    2025-12-18 10:56:38 SMTP ERROR: Failed to connect to server: (0)Code language: Access log (accesslog)

    关键信息在这里:OpenSSL Error messages:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed

    OpenSSL 证书验证失败,进而无法加密,导致无法连接 SMTP 服务器。

    为什么会验证证书会失败?这里有几种可能,其中一种可能是远端 SMTP 服务器配置有误,导致我们无法验证对方的证书。另一种可能是我们这边服务器配置有误。

    首先在自己的电脑上快速检查远端 SMTP 服务器证书是否有问题:

    openssl s_client -connect 服务器地址:端口号 -showcertsCode language: Shell Session (shell)
    # openssl s_client -connect smtpdm.aliyun.com:465 -showcerts
    CONNECTED(00000003)
    depth=2 OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
    verify return:1
    depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign GCC R3 OV TLS CA 2024
    verify return:1
    depth=0 C = CN, ST = ZheJiang, L = HangZhou, O = "Alibaba (China) Technology Co., Ltd.", CN = mail.aliyun.com
    verify return:1
    ---
    Certificate chain
     0 s:C = CN, ST = ZheJiang, L = HangZhou, O = "Alibaba (China) Technology Co., Ltd.", CN = mail.aliyun.com
       i:C = BE, O = GlobalSign nv-sa, CN = GlobalSign GCC R3 OV TLS CA 2024
       a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
       v:NotBefore: Jul 30 07:21:02 2025 GMT; NotAfter: Aug 31 07:21:01 2026 GMT
    -----BEGIN CERTIFICATE-----
    MIIbVzCCGj+gAwIBAgIMNqmX8xa3aRIoP525MA0GCSqGSIb3DQEBCwUAMFMxCzAJ
    ......
    +czIqWmHPBZzUHJ3514papBAaldNzuAaMi+ggOn2AK4/LSVA2h9Q5I3Q9Q==
    -----END CERTIFICATE-----
     1 s:C = BE, O = GlobalSign nv-sa, CN = GlobalSign GCC R3 OV TLS CA 2024
       i:OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
       a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
       v:NotBefore: Sep 18 03:14:38 2024 GMT; NotAfter: Mar 18 00:00:00 2029 GMT
    -----BEGIN CERTIFICATE-----
    MIIEmDCCA4CgAwIBAgIRAIHlq5jkbzW5HC/6F4cYyFowDQYJKoZIhvcNAQELBQAw
    ......
    JI6wy6Il7Dat8NzQpJmYLGcaiSTY3TZl51f4xw==
    -----END CERTIFICATE-----
     2 s:OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
       i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
       a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
       v:NotBefore: Sep 19 00:00:00 2018 GMT; NotAfter: Jan 28 12:00:00 2028 GMT
    -----BEGIN CERTIFICATE-----
    MIIETjCCAzagAwIBAgINAe5fFp3/lzUrZGXWajANBgkqhkiG9w0BAQsFADBXMQsw
    ......
    4HA=
    -----END CERTIFICATE-----
    ---
    Server certificate
    subject=C = CN, ST = ZheJiang, L = HangZhou, O = "Alibaba (China) Technology Co., Ltd.", CN = mail.aliyun.com
    issuer=C = BE, O = GlobalSign nv-sa, CN = GlobalSign GCC R3 OV TLS CA 2024
    ---
    No client certificate CA names sent
    Peer signing digest: SHA256
    Peer signature type: RSA-PSS
    Server Temp Key: X25519, 253 bits
    ---
    SSL handshake has read 9855 bytes and written 399 bytes
    Verification: OK
    ---
    New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
    Server public key is 2048 bit
    Secure Renegotiation IS NOT supported
    Compression: NONE
    Expansion: NONE
    No ALPN negotiated
    Early data was not sent
    Verify return code: 0 (ok)
    ---
    ---
    Post-Handshake New Session Ticket arrived:
    SSL-Session:
        Protocol  : TLSv1.3
        Cipher    : TLS_AES_256_GCM_SHA384
        Session-ID: CE91F7......0E4FCB4
        Session-ID-ctx:
        Resumption PSK: 13EA0......C071
        PSK identity: None
        PSK identity hint: None
        SRP username: None
        TLS session ticket lifetime hint: 7200 (seconds)
        TLS session ticket:
        0000 - 43 07 31 f3 a5 0c 52 88-e3 fc c4 96 c1 3c 30 a3   C.1...R......<0.
        ......
        00b0 - a1 c5 12 34 1c 88 6f 05-81 4a 1b 61 67 ae 2e 1f   ...4..o..J.ag...
    
        Start Time: 17......67
        Timeout   : 7200 (sec)
        Verify return code: 0 (ok)
        Extended master secret: no
        Max Early Data: 0
    ---
    read R BLOCK
    ---
    Post-Handshake New Session Ticket arrived:
    SSL-Session:
        Protocol  : TLSv1.3
        Cipher    : TLS_AES_256_GCM_SHA384
        Session-ID: 56AF......203A
        Session-ID-ctx:
        Resumption PSK: 0A45......2AEA
        PSK identity: None
        PSK identity hint: None
        SRP username: None
        TLS session ticket lifetime hint: 7200 (seconds)
        TLS session ticket:
        0000 - 43 07 31 f3 a5 0c 52 88-e3 fc c4 96 c1 3c 30 a3   C.1...R......<0.
        ......
        00b0 - 99 06 be 74 88 48 a9 7f-09 73 6a 44 9f 1d e0 d2   ...t.H...sjD....
    
        Start Time: 1765537767
        Timeout   : 7200 (sec)
        Verify return code: 0 (ok)
        Extended master secret: no
        Max Early Data: 0
    ---
    read R BLOCK
    220 DirectMail Smtpd Server(127.0.0.1)Code language: Shell Session (shell)

    可以看到远端 SMTP 服务器配置没有问题,证书链是完整的,而且也都通过了验证,那问题应该是出在我们这边的 Web 服务器。

    WordPress 部署在阿里云虚拟主机上,推测是 CA 根证书配置有问题,导致无法验证 SMTP 服务器证书。

    首先查看 phpinfo,如下图:

    阿里云虚拟主机phpinfo 显示 openssl.cafile 和 openssl.capath 都是空的

    可以看到 openssl.cafileopenssl.capath 都是空的,这是 PHP 默认值。

    尝试添加CA证书。Mozilla 在 wiki 页面上给出了 CA 根证书 Store,我这里下载 PEM of Root Certificates in Mozilla’s Root Store with the Websites (TLS/SSL) Trust Bit Enabled (TXT),命名为 ca-certificates.crt

    把从 Mozilla 下载的根证书 Store 上传到服务器上,然后修改 php.ini.user.ini 文件,设置 openssl.cafile 的值为 CA 根证书 Store 路径。

    比如我把下载的 CA 根证书文件命名为 ca-certificates.crt,放在 /usr/home/ 目录下,那么 CA 根证书 Store 的路径就是,/usr/home/ca-certificates.crt,在 php.ini.user.ini 文件加入下面这行即可:

    openssl.cafile = "/usr/home/ca-certificates.crt"Code language: plaintext (plaintext)

    最后再次测试,邮件发送成功,邮箱也收到了邮件,问题解决。

    参考资料:

  • 阿里云怎么使用微信付款?续费域名微信支付的方式

    eulerkey.cn 这个域名目前放在阿里云,但是阿里云官网付款方式并没有微信支付,支付方式只有支付宝、花呗(还是支付宝)、银联和网上银行。

    阿里云官网收银台支付方式截图,仅支持支付宝、银联和网上银行

    那是不是就不能通过微信支付了呢?我想到阿里云还有手机 App,说不定可以从那里用微信支付。然而阿里云 App 还是不支持微信支付,付款方式依旧是支付宝、花呗(还是支付宝)和银联云闪付。

    阿里云 App 收银台截图,不支持微信支付

    准备放弃了?再去微信小程序看看,微信里总不能还不支持微信支付吧?搜索「阿里云万网」微信小程序,在这里可以用微信付款续费域名,还可以使用微信支付的优惠和立减金。

    阿里云万网微信小程序支付界面,支持微信支付续费域名,还可以使用微信支付的优惠和立减金
  • 阿里云虚拟主机禁用 sleep 函数?fatal error 报错解决方法

    我的好几个 WordPress 插件都因为这个函数的缺失导致 fatal error。

    Fatal error: Uncaught Error: Call to undefined function sleep() in /usr/home/htdocs/wp-content/plugins/wp-super-cache/partials/easy.php:79 Stack trace: #0 /usr/home/htdocs/wp-content/plugins/wp-super-cache/wp-cache.php(4423): include() #1 /usr/home/htdocs/wp-content/plugins/wp-super-cache/wp-cache.php(1311): wpsc_render_partial('partials/easy.p...', Array) #2 /usr/home/htdocs/wp-includes/class-wp-hook.php(341): wp_cache_manager('') #3 /usr/home/htdocs/wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters('', Array) #4 /usr/home/htdocs/wp-includes/plugin.php(522): WP_Hook->do_action(Array) #5 /usr/home/htdocs/wp-admin/admin.php(264): do_action('settings_page_w...') #6 /usr/home/htdocs/wp-admin/options-general.php(10): require_once('/usr/home/wh-aa...') #7 {main} thrown in /usr/home/htdocs/wp-content/plugins/wp-super-cache/partials/easy.php on line 79Code language: Access log (accesslog)

    看了 phpinfo() 发现 sleep 函数被禁用了,我没想到这个无害的函数会被禁用,估计插件作者也不会想到这个函数会被禁用吧。

    phpinfo() 显示的 disable_functions

    从图中可以看出,阿里云虚拟主机禁用的 PHP 函数如下:

    exec,system,passthru,shell_exec,escapeshellarg,escapeshellcmd,proc_close,proc_open,ini_alter,dl,popen,pcntl_exec,socket_accept,socket_bind,socket_clear_error,socket_close,socket_connect,socket_create_listen,socket_create_pair,socket_create,socket_get_option,socket_getpeername,socket_getsockname,socket_last_error,socket_listen,socket_read,socket_recv,socket_recvfrom,socket_select,socket_send,socket_sendto,socket_set_block,socket_set_nonblock,socket_set_option,socket_shutdown,socket_strerror,socket_write,stream_socket_client,stream_socket_server,pfsockopen,disk_total_space,disk_free_space,chown,diskfreespace,getrusage,get_current_user,getmyuid,getmypid,dl,leak,listen,chgrp,link,symlink,dlopen,proc_nice,proc_get_stats,proc_terminate,shell_exec,sh2_exec,posix_getpwuid,posix_getgrgid,posix_kill,ini_restore,mkfifo,dbmopen,dbase_open,filepro,filepro_rowcount,posix_mkfifo,putenv,sleepCode language: plaintext (plaintext)

    这里大部分函数被禁用是出于安全考虑,比如文件操作、系统命令执行、进程控制、网络操作等函数被禁用,这是是可以理解的,但是 sleep 函数也被禁用实在让人想不通。

    我首先想到的应对措施是能不能用 .user.ini 文件重新启用 sleep 函数。但是根据 PHP 官方文档的说明,disable_functions 只能在全局 php.ini 文件中设置,.user.ini 文件中无效。

    不过文档也说:As of PHP 8.0.0, disabling a function removes its definition, allowing userland to redefine it.

    也就是说在 PHP 8.0 及以上版本中,禁用的函数可以在用户代码中重新定义。所以我自己模拟了一个 sleep 函数,代码如下:

    <?php
    if (!function_exists('sleep')) {
        function sleep($seconds) {
            $start = time();
            while (time() - $start < $seconds);
            return 0;
        }
    }Code language: PHP (php)

    将这段代码保存为 sleep.php,然后在 .user.ini 文件中添加 auto_prepend_file 配置指向这个 sleep.php 文件路径,比如:

    auto_prepend_file = "/usr/home/sleep.php"Code language: plaintext (plaintext)

    这样每次PHP脚本运行时,都会先包含这个 sleep.php 文件,从而定义 sleep 函数,避免 fatal error。

    阿里云这种做法真是让人摸不着头脑,按理说禁用函数是为了安全性,把不安全的函数禁用掉是可以理解的,但一个 sleep 函数能造成什么安全隐患呢?这是最安全的函数之一了吧,毕竟它的功能就是等待一段时间,什么都不执行。

    我猜测,阿里云这么做是出于性能/经济考虑,禁用这个函数试图减少虚拟主机用户占用的资源,但这种做法对谁都没好处。对用户来说,用户如果要用到这个函数,只能自己模拟,不然就等着程序 fatal error。对阿里云来说,模拟函数的开销肯定比原生函数的开销大,本来想着“降本增效”,最后反倒可能浪费更多资源。

    参考资料:

  • 阿里云虚拟主机安装 WordPress 显示此站点遇到了致命错误

    可能的原因:PHP chmod 函数被禁用,WordPress 无法正常修改文件权限。

    解决方法:登录主机管理控制台,依次选择高级环境设置->PHP.INI 设置PHP 函数 chmod 设置选择启动,单击保存设置即可。

    阿里云主机控制面板 PHP 函数 chmod 设置