Docker-Compose应用中跨域请求被拦截问题求助
你遇到的核心问题不是CORS配置错误,而是浏览器不在Docker的内部网络中,无法识别backend这个Docker服务名。容器内curl/ping能通是因为容器处于Docker默认创建的桥接网络里,Docker DNS会把服务名解析成对应容器的IP,但浏览器运行在宿主机系统,它的DNS无法解析这个内部域名,导致请求根本无法发送到后端,进而触发了CORS相关的报错(本质是请求失败的次生错误)。
下面提供几种可行的解决办法,按复杂度从低到高排序:
方案1:直接使用宿主机地址访问后端
修改前端的请求地址,把http://backend:9000/api/weather改成http://localhost:9000/api/weather(如果是远程部署,换成宿主机的公网IP/域名)。
同时调整后端的CORS配置,更精准地允许前端的源(避免*的潜在问题):
const cors = require('kcors'); const app = new Koa(); app.use(cors({ origin: 'http://localhost:8000' // 前端运行的地址 }));
这样浏览器就能通过宿主机映射的9000端口访问后端,同时CORS配置允许前端的源,请求就能正常完成。
方案2:配置React开发代理
如果是开发环境,可以利用React自带的代理功能,避免直接暴露后端地址,同时解决跨域问题:
- 在前端项目的
package.json中添加一行:
"proxy": "http://backend:9000"
- 修改前端的请求代码,去掉域名部分,直接请求相对路径:
const getWeatherFromApi = async () => { try { const response = await fetch('/api/weather'); return response.json(); } catch (error) { console.error(error); } return {}; };
这样前端开发服务器会把/api开头的请求代理到http://backend:9000,浏览器只和前端服务(localhost:8000)通信,不存在跨域问题,同时利用Docker内部网络解析backend服务名。
方案3:用Nginx反向代理统一域名(生产环境推荐)
如果是生产环境,建议用Nginx做反向代理,把前端和后端放在同一个域名下,彻底消除跨域问题:
- 在
docker-compose.yml中添加Nginx服务:
version: "3.8" services: frontend: build: ./frontend container_name: frontend # 不需要对外暴露端口,让Nginx访问 environment: - PORT=8000 - HOST=0.0.0.0 backend: build: ./backend container_name: backend # 不需要对外暴露端口,让Nginx访问 environment: - PORT=9000 - MAP_ENDPOINT=http://api.openweathermap.org/data/2.5 - TARGET_CITY=Helsinki,fi - APPID=api_key_here nginx: image: nginx:alpine container_name: nginx-proxy ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf depends_on: - frontend - backend
- 创建
nginx.conf配置文件:
events {} http { server { listen 80; # 前端页面请求 location / { proxy_pass http://frontend:8000; proxy_set_header Host $host; } # 后端API请求 location /api { proxy_pass http://backend:9000; proxy_set_header Host $host; } } }
- 修改前端请求代码为相对路径:
const getWeatherFromApi = async () => { try { const response = await fetch('/api/weather'); return response.json(); } catch (error) { console.error(error); } return {}; };
这样用户访问http://localhost时,Nginx会把静态页面请求转发到前端,API请求转发到后端,所有请求都来自同一个域名,完全不存在跨域问题。
内容的提问来源于stack exchange,提问作者tranzzik

