access denied for user 是 mysql 权限问题,核心在于 user@host 组合不匹配;需确认连接地址(localhost vs 127.0.0.1)、验证账号权限、检查 host 精确匹配、注意 docker/云环境网络配置,并用 select user(), current_user() 定位实际匹配的权限记录。

php mysqli_connect 报错 Access denied for user
这是 MySQL 权限层面的拒绝,不是 PHP 代码写错了。错误信息里 Access denied for user 'xxx'@'yyy' 的 'xxx' 是你填的用户名,'yyy' 是 MySQL 认为的客户端来源(可能是 localhost、127.0.0.1 或具体 IP)。重点不是“密码输错了”,而是“这个用户根本没被授权从这个地址登录”。
- 检查
$host参数:用localhost会走 Unix socket,用127.0.0.1才走 TCP;两者在 MySQL 权限表里是不同记录,user@localhost≠user@127.0.0.1 - 确认实际连接地址:在 PHP 中打印
gethostname()或直接用var_dump($_SERVER['SERVER_ADDR'] ?? 'unknown')看脚本运行在哪台机器上 - 别靠“测试密码是否正确”来排查:MySQL 在用户名不存在或权限不匹配时,也会返回同样错误,掩盖真实原因
用 mysql -u 验证账号能否真正登录
跳过 PHP,直接在服务器命令行模拟连接,是最准的验证方式。它能绕过 PHP 配置、PDO 设置、连接池干扰等中间层。
- 执行
mysql -u your_user -p -h 127.0.0.1(注意加-h,否则默认走localhost) - 如果提示
ERROR 1045 (28000): Access denied,说明账号本身有问题;如果连不上(timeout 或 connection refused),说明 MySQL 没监听对应地址/端口 - 成功登录后,立刻执行
SELECT USER(), CURRENT_USER();—— 前者是你声明的身份,后者是 MySQL 实际匹配到的权限记录,两者不同就说明存在同名但 host 不同的账号冲突
CREATE USER 和 GRANT 必须匹配 host 字段
MySQL 的权限是按 user@host 组合存储的。你用 mysqli_connect('127.0.0.1', 'foo', 'bar'),就必须有 foo@'127.0.0.1' 这条记录,foo@'localhost' 完全无效。
- 建用户时别省略
HOST:用CREATE USER 'myapp'@'127.0.0.1' IDENTIFIED BY 'pwd';,而不是只写CREATE USER 'myapp' IDENTIFIED BY 'pwd';(后者默认@'%',但可能被更具体的规则覆盖) - 授予权限时必须用相同 host:先有
'myapp'@'127.0.0.1',再GRANT SELECT ON mydb.* TO 'myapp'@'127.0.0.1'; - 改完记得
FLUSH PRIVILEGES;,否则新权限不生效
PHP-FPM 与 MySQL 服务不在同一台机器时的常见陷阱
本地开发环境常忽略网络可达性。比如 Docker 容器里跑 PHP,宿主机跑 MySQL,默认 127.0.0.1 指的是容器自己的回环,根本连不到宿主机。
立即学习“PHP免费学习笔记(深入)”;
- Docker 场景下,PHP 容器要连宿主机 MySQL,
$host应设为host.docker.internal(Mac/Windows)或宿主机真实局域网 IP(Linux) - 云服务器上,检查安全组是否放行了 MySQL 默认端口
3306,且 MySQL 配置中bind-address没被锁死为127.0.0.1 - 某些托管环境(如 cPanel)限制外部连接,只允许
localhost,此时 PHP 必须和 MySQL 同机部署,$host只能用localhost
最麻烦的其实是权限记录重复或 host 匹配模糊——比如同时存在 user@'192.168.%' 和 user@'%',MySQL 会选最长匹配,但你根本不知道它选了哪个。查 SELECT User,Host FROM mysql.user; 比猜靠谱得多。











