MacOS下Docker部署Next.js+Nginx调用后端API出现net::ERR_FAILED问题
问题原因及解决方案
核心原因
浏览器无法识别Docker内部服务域名:
前端配置的NEXT_PUBLIC_API_BASE_URL=http://backend:5055/api中,backend是Docker Compose创建的内部服务域名,仅在容器所在的虚拟网络内可解析。但前端代码最终在浏览器(宿主机环境)运行,浏览器无法识别该内部域名,导致请求出现net::ERR_FAILED错误。SSR场景下的localhost认知误区:
当修改配置为http://localhost:5055/api时,Next.js的SSR代码会在frontend容器内执行,此时容器内的localhost指向容器自身而非宿主机,因此出现ECONNREFUSED错误(容器内未监听5055端口)。
验证依据
- Postman直接请求
http://localhost:5055正常,是因为Postman运行在宿主机,直接访问了Docker映射到宿主机的端口。 - Nginx容器内通过
curl backend:5055/api/category/show能正常请求,说明Docker内部网络的服务通信无问题。
解决方案
方案1:利用Nginx反向代理统一请求路径
根据现有Nginx配置,已设置location /api将请求转发到http://backend,只需修改前端配置:
# frontend - .env.local NEXT_PUBLIC_API_BASE_URL=/api
同时调整httpServices.js,区分客户端与服务端环境的API地址:
// frontend - httpServices.js import axios from 'axios' import Cookies from 'js-cookie' // 判断是否为服务端环境 const isServer = typeof window === 'undefined' const baseURL = isServer ? 'http://backend:5055/api' // 服务端用Docker内部域名 : '/api' // 客户端用相对路径 const instance = axios.create({ baseURL, timeout: 500000, headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, }) // ... 其余代码保持不变
这样:
- 客户端请求:浏览器发送请求到
http://localhost/api,由Nginx转发至backend容器。 - SSR服务端请求:frontend容器内的代码直接通过Docker内部域名访问backend。
方案2:单独配置服务端API地址
若不想依赖Nginx代理服务端请求,可添加非公开环境变量:
# frontend - .env.local NEXT_PUBLIC_API_BASE_URL=/api # 客户端使用 API_BASE_URL=http://backend:5055/api # 服务端使用,不带NEXT_PUBLIC前缀
在服务端代码(如getServerSideProps、服务端API路由)中使用process.env.API_BASE_URL,客户端代码使用process.env.NEXT_PUBLIC_API_BASE_URL。
总结
问题本质是运行环境的网络上下文差异:Docker内部服务名仅在容器网络内有效,浏览器(宿主机)与容器内的服务端代码处于不同网络环境,需分别配置对应API地址。
内容的提问来源于stack exchange,提问作者John Stuart
相关产品推荐
相关产品推荐

