Nginx 作为反向代理或负载均衡器,在处理请求时经常需要与后端服务建立 TCP 连接。当客户端访问 Nginx 时页面提示 502 Bad Gateway,而 Nginx 错误日志中出现 connect() failed (111: Connection refused) while connecting to upstream,这个 connection refused 并不是 Nginx 自己的报错,而是操作系统在尝试建立 TCP 连接时,目标主机返回了 RST 报文。理解这一点是快速定位问题的关键。

一、connection refused 的底层含义与常见触发场景
TCP 连接被拒绝(connection refused)发生在三次握手阶段。客户端发送 SYN 包后,如果目标主机的内核发现该端口没有任何进程处于 LISTEN 状态,或者防火墙规则直接拦截了该端口的入站请求,内核会回复一个 RST 包。客户端收到 RST 后,socket 连接立即失败,错误码通常为 ECONNREFUSED,对应 Nginx 日志中的 111 号错误。也就是说,connection refused 本质上是目标主机明确告诉源主机:这个端口现在不接受连接。
常见触发场景有四种。第一种是后端服务根本没有启动,端口自然无人监听。第二种是后端进程监听了 127.0.0.1 的某个端口,而 Nginx 配置的 proxy_pass 指向的是其他 IP 地址,例如容器内监听 0.0.0.0 与监听 127.0.0.1 的差别。第三种是防火墙或安全组拦截了请求,虽然端口有进程监听,但包到不了应用层。第四种是后端监听的是 IPv6 地址,而 Nginx 尝试用 IPv4 地址连接,或者反过来。定位问题时,先分清 Nginx 是作为客户端去连接别人,还是作为服务端接受连接,两者思路完全不同。
二、使用系统工具快速确认拒绝来源
在怀疑后端服务之前,先确认后端端口到底有没有进程在听。使用 ss -tlnp 可以查看所有 TCP 监听端口以及对应的进程名。如果端口没有出现在列表中,说明后端进程没起来,或者监听地址不对。比如端口 8080 只出现在 127.0.0.1 这一行,而 Nginx 通过内网 IP 192.168.1.10 去连 8080,就会得到 connection refused。
# 查看所有TCP监听端口 ss -tlnp # 查看指定端口的监听情况 ss -tlnp | grep ':8080' # 用telnet测试端口连通性 telnet 127.0.0.1 8080 # 用curl查看后端HTTP响应 curl -v http://127.0.0.1:8080/
如果 ss 显示端口正在监听,但 telnet 仍然提示 connection refused,就需要检查防火墙规则。在 CentOS 或 RHEL 上可以使用 firewall-cmd --list-all 查看当前区域放行的端口,在 Ubuntu 上通常使用 ufw status。还有一种容易被忽略的情况是 SELinux 或 AppArmor 对特定进程的网络访问做了限制,可以通过 getenforce 和 audit2why 辅助判断。确认防火墙阶段没有问题后,再回到 Nginx 配置层面继续排查。
另外,如果后端服务由 Docker 容器提供,需要注意容器端口映射关系。宿主机上的端口可能映射到了容器内部,但如果容器内进程只监听 127.0.0.1,而映射时使用的是 0.0.0.0,外部访问宿主机 IP 可能仍会被拒绝。此时进入容器内部执行 ss -tlnp 查看监听地址,有助于发现地址绑定的差异。
三、Nginx配置中常见的引起connection refused的误区
Nginx 配置里最容易出问题的是 proxy_pass 和 upstream 中的地址写错。例如后端服务实际监听在 8080 端口,但配置中写成了 8081,Nginx 连接后端时自然会收到拒绝。另一个常见问题是使用 localhost 时,系统优先解析为 IPv6 地址 ::1,而后端服务只监听了 IPv4 的 127.0.0.1,导致 Nginx 尝试连接 ::1:8080 失败。解决方法是明确写成 127.0.0.1,或者统一监听地址。
http {
upstream backend {
# 错误:后端只监听127.0.0.1,这里却写localhost可能解析为::1
# server localhost:8080;
# 正确:明确指定IPv4地址
server 127.0.0.1:8080;
}
server {
listen 80;
server_name ipipp.com;
location / {
proxy_pass http://backend;
proxy_connect_timeout 3s;
proxy_next_upstream error timeout http_502 http_503;
}
}
}
在 upstream 块中,如果多个后端服务器中有一台已经宕机,Nginx 默认不会自动跳过它,除非配置了 proxy_next_upstream 和合理的超时时间。遇到 connection refused 时,Nginx 会把这个错误视为 upstream 连接失败,如果配置了重试机制,它会尝试下一个后端;如果没有,就直接返回 502。因此,合理的超时和重试配置可以缓解单点后端故障带来的影响,但不能掩盖后端未启动的根本问题。
另一个容易混淆的点是 listen 指令。如果 Nginx 只监听 listen [::]:80;,而客户端通过 IPv4 地址访问,可能出现无法连接或异常。反过来,如果 Nginx 配置为 listen 80; 只监听 IPv4,但后端服务只监听 IPv6,也会引发 connection refused。确认 Nginx 和后端服务的 IP 协议版本是否匹配,是排查时必须做的一步。
四、从日志与抓包角度定位拒绝点
Nginx 错误日志是定位 connection refused 最直接的依据。在 error.log 中看到类似 connect() failed (111: Connection refused) while connecting to upstream 的信息,通常会同时给出 upstream 的具体地址和请求的 URL。通过这个地址可以快速判断是哪个后端节点拒绝连接。如果日志中显示 upstream 是 http://127.0.0.1:8080/,就去本机检查 8080 端口;如果显示的是内网 IP,则去对应服务器检查。
# 过滤Nginx错误日志中的connection refused grep 'Connection refused' /var/log/nginx/error.log # 实时跟踪错误日志 tail -f /var/log/nginx/error.log | grep --line-buffered 'connect() failed' # 使用tcpdump抓取到后端端口的TCP包 tcpdump -i any port 8080 -nn -S
如果 Nginx 日志信息不足,可以在 Nginx 所在服务器上抓包。使用 tcpdump 过滤目标端口,观察是否收到 RST 包。正常情况下,如果端口有进程监听,SYN 之后会收到 SYN-ACK;如果收到 RST,说明目标主机的网络栈拒绝了连接。抓包还能帮助判断 RST 是来自后端主机,还是来自中间的防火墙或安全设备。例如在后端服务器上同时抓包,如果 Nginx 那边显示发出 SYN,而后端没收到,则问题在网络上;如果后端收到并回复了 RST,则问题在后端应用或内核配置。
除了系统层抓包,后端应用自身的日志同样重要。比如后端是 Node.js、Python Flask 或 Java Spring Boot,有时进程虽然启动了,但应用初始化失败导致监听端口没有打开。此时查看应用启动日志,通常能看到绑定端口失败的异常堆栈。将应用日志、Nginx 错误日志和系统层抓包三者结合起来,基本可以覆盖绝大多数 connection refused 的根因。
五、修复策略与预防措施
修复 connection refused 的最直接方式就是确保后端服务先于 Nginx 启动,并且监听正确的地址和端口。对于生产环境,建议使用 systemd、supervisord 或容器编排工具管理后端进程,设置开机自启和崩溃自动拉起。如果是多节点后端,应配置健康检查,Nginx 的 upstream 模块虽然自带的健康检查功能有限,但可以通过 max_fails 和 fail_timeout 参数在失败多次后暂时剔除节点,避免请求持续打到宕机后端。
http {
upstream backend {
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_next_upstream error timeout http_502 http_503;
}
}
}
预防措施还包括统一监听地址约定。内部服务尽量监听 0.0.0.0 或明确的业务网卡 IP,避免只监听 127.0.0.1 导致跨机访问失败。对于容器化部署,需要明确容器网络模式,宿主机端口映射和容器内部监听地址之间的关系。同时,在防火墙策略变更或发布新版本后端时,建议建立端口监听检查步骤,比如在部署脚本中增加 ss -tlnp | grep 目标端口 的校验,失败则回滚。这样可以避免因为配置漂移或启动顺序问题再次引发 connection refused。
最后,阅读 Nginx 错误日志时,不要只关注 connection refused 这一条。有时后端服务会因为瞬时压力过大而拒绝新连接,比如 TCP backlog 已满,内核同样可能丢弃或拒绝 SYN。这种情况需要调整后端服务的监听 backlog 参数,或者优化应用处理速度。将 connection refused 视作一个系统协作问题,而不是孤立的 Nginx 配置错误,才能从根本上降低故障复发概率。

