WordPress 连接 vsftpd 报“用户名密码错误”问题完整复盘

(最终采用 FTP + FTP_SYS_TYPE 方案)

一、问题现象

WordPress 后台安装/更新主题或插件时反复提示:

安装失败:用户名和密码错误(unable_to_connect_to_filesystem)

但:

  • 使用 FTP 客户端(FileZilla、lftp)连接成功
  • 服务器本地 ftp localhost 登录成功
  • 独立 PHP 脚本调用 ftp_connect() + ftp_login() 也成功

二、环境信息

组件配置
服务器阿里云 ECS(Ubuntu/Debian 系)
Web 服务器Apache(运行用户 www-data)
FTP 服务vsftpd 3.0.5
FTP 用户wp-user,根目录 /var/www/html
WordPressFS_METHOD = 'ftpext'(使用 PHP FTP 扩展)

三、排查过程

3.1 检查 vsftpd 监听地址

sudo netstat -tlnp | grep :21
tcp6       0      0 :::21                   :::*                    LISTEN      13814/vsftpd

问题:vsftpd 默认 listen_ipv6=YES,只监听 IPv6(:::21),而 PHP 的 ftp_connect() 默认使用 IPv4,导致连接被拒绝。

解决:修改 /etc/vsftpd.conf

listen=YES
listen_ipv6=NO

重启 vsftpd:sudo systemctl restart vsftpd

3.2 验证 IPv4 监听生效

sudo netstat -tlnp | grep :21

输出:

tcp        0      0 0.0.0.0:21              0.0.0.0:*               LISTEN

3.3 独立 PHP 脚本测试(绕过 WordPress)

创建 /var/www/html/ftptest.php:

<?php
$conn = ftp_connect('127.0.0.1', 21);
if (!$conn) die("连接失败");
if (ftp_login($conn, 'wp-user', 'your_password')) {
    echo "登录成功,当前目录: " . ftp_pwd($conn);
} else {
    echo "登录失败";
}
?>

访问脚本 → 输出 登录成功,当前目录: /var/www/html,证明 PHP FTP 扩展和 vsftpd 工作正常。

3.4 观察 vsftpd 日志(定位 WordPress 行为)

sudo tail -f /var/log/vsftpd.log

当 WordPress 尝试更新时,日志仅显示:

CONNECT: Client "::ffff:127.0.0.1"
OK LOGIN: Client "::ffff:127.0.0.1"

没有后续的 CWD、LIST 等命令,说明 WordPress 在登录后立即判定失败。

3.5 启用 WordPress 调试

在 wp-config.php 中添加:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);
@ini_set('display_errors', 1);

再次尝试更新,页面仍只显示“用户名密码错误”,无额外 PHP 警告。

3.6 分析 WordPress FTP 类源码

查看 wp-admin/includes/class-wp-filesystem-ftpext.php,连接流程包含:

$this->systype = ftp_systype( $this->link );

vsftpd 返回的系统类型字符串为 “215 UNIX Type: L8″,而 WordPress 内部期望特定格式(如 “UNIX”)。当 ftp_systype() 返回值不符合预期时,WordPress 可能抛出异常,被上层捕获后笼统地报告为“用户名密码错误”。

四、最终解决方案(成功)

在 wp-config.php 中强制指定系统类型,绕过 ftp_systype() 检测。

同时因为 vsftpd 已配置 chroot_local_user=YES 和 local_root=/var/www/html,登录后根目录即为 /var/www/html,故设置 FTP_BASE = ‘/’。

完整有效配置:

/* FTP 连接设置 */
define('FS_METHOD', 'ftpext');
define('FTP_HOST', '127.0.0.1:21');
define('FTP_USER', 'wp-user');
define('FTP_PASS', 'your_password');
define('FTP_SSL', false);
define('FTP_PASV', true);
define('FTP_BASE', '/');
define('FTP_SYS_TYPE', 'UNIX');   // 关键

修改后,WordPress 后台安装/更新主题、插件均正常,不再提示连接错误。

五、根因总结

现象实际原因误导信息
WordPress 报“用户名密码错误”ftp_systype() 返回值与预期不符,导致 WordPress 内部处理异常FTP 客户端和独立脚本均能正常登录

核心点:WordPress 的错误消息不够精确,当文件系统连接初始化失败时,常常笼统地报告“用户名密码错误”,掩盖了真实原因。

六、WordPress FTP 故障排查清单

6.1 快速检查项

  • vsftpd 是否同时监听 IPv4 和 IPv6?
    → netstat -tlnp | grep :21 应包含 0.0.0.0:21。若只有 :::21,修改 listen_ipv6=NO。
  • 防火墙是否放行 21 端口及被动模式端口段?
    → 阿里云安全组 + 系统防火墙(UFW/firewalld/iptables)。
  • FTP 用户是否被列入黑名单?
    → 检查 /etc/vsftpd.ftpusers 和 /etc/vsftpd.user_list。
  • 目录权限和所有者是否正确?
    → chown -R wp-user:www-data /var/www/html,目录 755,文件 644,wp-content 775。
  • PHP FTP 扩展是否安装?
    → php -m | grep ftp。

6.2 独立 PHP 脚本测试(黄金法则)

<?php
$conn = ftp_connect('127.0.0.1', 21, 5);
if (!$conn) die("连接失败");
if (ftp_login($conn, 'wp-user', 'password')) {
    echo "登录成功,当前目录: " . ftp_pwd($conn);
    echo " 系统类型: " . ftp_systype($conn);
} else {
    echo "登录失败";
}
?>

脚本成功 → 问题在 WordPress 层(多半是 FTP_SYS_TYPE 或路径常量)

脚本失败 → 检查 vsftpd 配置、网络、防火墙

6.3 WordPress 配置常量速查表

常量推荐值说明
FS_METHOD‘ftpext’使用 PHP FTP 扩展
FTP_HOST‘127.0.0.1:21’本机 FTP 用回环地址
FTP_USER / FTP_PASS实际凭据
FTP_SSLfalsevsftpd 未配置 SSL
FTP_PASVtrue被动模式
FTP_BASE‘/’当 vsftpd 启用了 chroot 且 local_root 指向 WordPress 根目录
FTP_SYS_TYPE‘UNIX’强制指定系统类型,绕过 ftp_systype() 检测
FTP_TIMEOUT30可选,增加超时时间

6.4 vsftpd 配置关键项

listen=YES
listen_ipv6=NO                 # 确保 IPv4 监听
local_enable=YES
write_enable=YES
chroot_local_user=YES
allow_writeable_chroot=YES     # 允许写入 chroot 目录
local_root=/var/www/html       # 指向 WordPress 根目录
pasv_enable=YES
pasv_min_port=50000
pasv_max_port=50010
pasv_address=公网IP             # 如需外网被动连接

七、调试技巧汇总

查看 vsftpd 日志(实时):

sudo tail -f /var/log/vsftpd.log

查看 Apache 错误日志:

sudo tail -f /var/log/apache2/error.log

开启 WordPress 调试:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);

检查 PHP 禁用函数:

php -i | grep disable_functions

检查监听端口和地址族:

netstat -tlnp | grep :21

八、结论

  • WordPress FTP 连接失败并提示“用户名密码错误”,并不一定是凭证问题。排查时应:
  • 先通过独立 PHP 脚本验证底层 FTP 是否可用。
  • 确认 vsftpd 监听 IPv4(或 PHP 能使用的地址族)。
  • 若底层正常而 WordPress 报错,优先尝试添加 define(‘FTP_SYS_TYPE’, ‘UNIX’);。
  • 同时检查 FTP_BASE 是否匹配 chroot 后的路径。

核心经验:WordPress 的错误信息可能具有误导性,需要结合日志和独立脚本进行分解测试,而非局限于其表面提示。本次最终成功方案是 FTP + FTP_SYS_TYPE,并未使用 SFTP。

发表回复