nginx的http模块不能代理mysql,因为mysql基于tcp二进制协议,不走http,导致location和proxy_pass无效;必须启用第四层的stream模块进行纯tcp透传,且配置需与http块平级、不可解析内容。

为什么 Nginx 的 HTTP 模块不能代理 MySQL
MySQL 是基于 TCP 的二进制协议,不走 HTTP,所以 location 和 proxy_pass http:// 完全无效。强行配置只会返回 502 或连接直接被拒绝——因为 Nginx 根本没把流量转给后端 MySQL 端口,而是在 HTTP 层就卡死了。
必须启用 Nginx 的 stream 模块(非默认编译进内核,需确认或重编译),它工作在 OSI 第四层,只做 TCP/UDP 流量转发,不解析内容。
- 检查是否已启用:
nginx -V 2>&1 | grep -o with-stream,无输出则需重新编译或换发行版(如 nginx-full) -
stream配置不能写在http{}块里,必须是顶层块,和http{}并列 - 转发过程无日志、无缓存、无重写能力,纯透传——这也是它适合数据库代理的原因
stream 模块最小可用配置示例
假设内网 MySQL 在 192.168.10.5:3306,你想让 Nginx(监听 0.0.0.0:3307)作为跳板供其他内网机器连接:
stream {
upstream mysql_backend {
server 192.168.10.5:3306;
}
server {
listen 3307;
proxy_pass mysql_backend;
proxy_timeout 1h;
proxy_responses 1;
}
}
注意几个硬性要求:
-
listen必须指定端口,不能写3307 ssl(MySQL 不走 TLS 握手,加 ssl 会导致客户端连不上) -
proxy_timeout建议设大些,默认 60s,长事务或导数据时容易断连 -
proxy_responses 1表示等待至少 1 个响应包再关闭连接,避免 MySQL 协议握手阶段被误判失败 - 别加
proxy_protocol on,除非你确定客户端支持并启用了 PROXY 协议(比如用 HAProxy 做前置)
常见连不上问题排查顺序
客户端执行 mysql -h nginx_ip -P 3307 -u user -p 报错时,按这个顺序查:
- 用
telnet nginx_ip 3307看端口通不通:不通 → 检查 Nginx 是否运行、firewalld/iptables是否放行、SELinux 是否拦截(setsebool -P nginx_can_network_connect 1) - 能 telnet 通但 mysql 连接卡住或报
Lost connection to MySQL server at 'reading initial communication packet'→ 检查upstream地址是否写错,MySQL 是否绑定了127.0.0.1而非0.0.0.0或具体内网 IP - MySQL 日志里出现
Access denied for user但账号密码没错 → 很可能是 MySQL 的bind-address允许了该 IP,但 Nginx 代理后,MySQL 看到的 client IP 是 Nginx 本机(如127.0.0.1),不是原始客户端 IP,权限要按 Nginx 机器 IP 授权
安全与生产注意事项
Stream 代理 MySQL 本质是裸 TCP 透传,不加密、不鉴权、不审计,仅适用于可信内网:
- 禁止将
stream配置暴露到公网,哪怕加了防火墙规则也不建议;MySQL 协议本身无传输加密,抓包即得明文密码 - 如果必须跨网络段,优先考虑 SSH 端口转发(
ssh -L 3307:192.168.10.5:3306 user@nginx_host)或专用隧道工具(如gost) - Nginx stream 不支持健康检查(
health_check是 HTTP 模块的),MySQL 实例挂了,Nginx 仍会把连接转过去,客户端超时才感知——需配合外部脚本 +nginx -s reload临时摘除节点 - 不要在 stream 块里配
log_format或access_log,它不支持;想记录连接行为,只能靠系统级工具如tcpdump或 MySQL 自身的 general_log(慎开)
真正麻烦的从来不是配置几行 stream,而是搞清谁连谁、IP 怎么变、权限怎么配、出问题往哪查——这些细节比语法重要得多。











