Docker中Next.js使用React-Query预取请求报ECONNREFUSED如何解决
问题根因
这个报错本质是搞混了Next.js两类代码的运行环境,两者的网络上下文完全不通用:
- 浏览器侧执行的代码(普通页面渲染、useEffect、客户端触发的React Query请求)跑在你宿主机的浏览器里,能识别宿主机的
localhost,但根本不知道Docker内部网络里的server服务名,所以你把地址改成http://server:5000/api的时候浏览器直接找不到主机,必然请求失败。 getServerSideProps、React Query服务端预取这类代码跑在ui容器的Node.js进程内部,这时候代码里写的127.0.0.1指向的是ui容器自己,这个容器里根本没跑5000端口的服务,自然会抛ECONNREFUSED连接拒绝的错;但因为你的ui、server两个容器都在同一个Docker桥接网络下,容器内部可以直接通过server这个服务名解析到后端容器的内网地址,所以这个场景下用http://server:5000反而能通。
最优方案:Next.js 同源重写统一请求入口
这个方案不需要写任何环境判断逻辑,前后端所有请求统一用相对路径/api作为基础地址,不管请求是服务端发的还是客户端发的都能正常走,还能顺带解决跨域问题,维护成本最低。
操作步骤:
- 打开Next.js项目根目录的
next.config.js配置文件,添加重写规则,原有配置保留即可:
/** @type {import('next').NextConfig} */ const nextConfig = { // 你原来的其他配置项不动 async rewrites() { return [ { source: '/api/:path*', // 重写逻辑在Next.js的Node进程里执行,能直接解析Docker内部的server服务名 destination: 'http://server:5000/api/:path*', }, ] }, } module.exports = nextConfig
- 把项目里所有接口请求的baseURL统一改成
/api即可,不需要再写任何全路径地址。
原理很简单:
- 浏览器发请求的时候,会先把请求打到Next.js服务的3000端口,Next.js自动匹配到
/api开头的路径,转发到后端服务,浏览器全程感知不到后端的真实地址,不会有解析失败的问题。 - 服务端侧发起的请求,同样会经过Next.js的路由规则处理,自动转发到Docker内部的后端服务地址,不需要手动写服务名。
- 后续如果要部署到其他环境,只需要修改重写规则里的目标地址就行,业务代码完全不用动。
快速兼容方案:动态判断运行环境切换地址
如果不想调整Next.js配置,也可以在封装Axios/请求实例的时候,直接判断当前运行环境选择对应的baseURL:
import axios from 'axios' // 服务端环境下window对象不存在,依此判断环境 const isServerSide = typeof window === 'undefined' const request = axios.create({ baseURL: isServerSide ? 'http://server:5000/api' : 'http://localhost:5000/api' })
这个方案的缺点是需要额外给Express后端配置CORS跨域允许规则,而且后续如果服务部署地址、容器服务名变动,需要同步修改这里的硬编码逻辑,长期维护不如重写方案方便。
另外你当前的docker-compose配置没有问题,所有服务都在同一个默认桥接网络下,容器间本来就可以通过服务名互相访问,不需要额外调整网络配置。
内容的提问来源于stack exchange,提问作者Rvfvl
相关产品推荐
相关产品推荐

