配置NGINX+Docker部署React前端与.NET后端:解决504网关超时
我在VPS上用Docker部署了包含React(Vite)前端、.NET后端和Nginx反向代理的项目,文件结构如下:
project/ ├── docker-compose.yml ├── example.client/ │ ├── dockerfile │ └── build/ ├── example.server/ │ ├── dockerfile │ └── app/ └── nginx/ ├── default.conf ├── nginx.conf └── certs ├── api.example.com │ ├── privkey.pem │ └── fullchain.pem └── example.com ├── privkey.pem └── fullchain.pem
预期访问方式:
- 前端通过
https://example.com访问 - 后端通过
https://api.example.com访问
两者均使用Let's Encrypt证书实现HTTPS。
目前前端部署成功,但前端发起请求时地址为https://example.com/api而非https://api.example.com/api,导致504网关超时。
已尝试:
- 验证后端在本地
https://localhost:5001可正常运行; - 确认证书已正确挂载到Docker容器。
相关配置文件如下:
Nginx配置(default.conf)
server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name example.com www.example.com; ssl_certificate /etc/nginx/certs/example.com/fullchain.pem; ssl_certificate_key /etc/nginx/certs/example.com/privkey.pem; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; location / { proxy_pass http://frontend:80; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } location /api/ { proxy_pass https://api.example.com/api/; proxy_set_header Host api.example.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 90; proxy_connect_timeout 90; proxy_send_timeout 90; } access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; } server { listen 80; server_name api.example.com www.api.example.com; return 301 https://$host$request_uri; } server { listen 443; server_name api.example.com; ssl_certificate /etc/nginx/certs/api.example.com/fullchain.pem; ssl_certificate_key /etc/nginx/certs/api.example.com/privkey.pem; location / { proxy_pass https://api.example.com:5001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } error_log /var/log/nginx/api_error.log; access_log /var/log/nginx/api_access.log; }
docker-compose.yml
version: '3.9' services: backend: image: example.server container_name: example.server build: context: ./example.server dockerfile: dockerfile ports: - "5000:5000" - "5001:5001" environment: - ASPNETCORE_URLS=https://+:5001 - ASPNETCORE_Kestrel__Certificates__Default__Path=/certs/fullchain.pem - ASPNETCORE_Kestrel__Certificates__Default__KeyPath=/certs/privkey.pem volumes: - /etc/letsencrypt/archive/api.example.com:/certs:ro networks: - app-network frontend: image: example.client container_name: example.client build: context: ./example.client dockerfile: dockerfile ports: - "3000:80" depends_on: - backend networks: - app-network volumes: - /etc/letsencrypt/archive/api.example.com:/etc/ssl/certs/api.example.com:ro - /etc/letsencrypt/archive/example.com:/etc/ssl/certs/example.com:ro nginx: image: nginx:latest container_name: nginx restart: always ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro - ./nginx/certs/:/etc/nginx/certs:ro depends_on: - frontend - backend networks: - app-network networks: app-network: driver: bridge
请问问题出在NGINX配置还是Docker网络?如何解决504网关超时问题?
问题核心在于Nginx配置的循环代理和错误的服务访问方式,与Docker网络本身无关,具体修复步骤如下:
1. 修复Nginx的循环代理问题
当前配置中,example.com的/api/ location代理到https://api.example.com/api/,而api.example.com的server块又代理到https://api.example.com:5001。这种配置会导致Nginx在容器内部尝试访问外部域名api.example.com,触发网络路由问题甚至循环请求,最终导致504超时。
正确做法是利用Docker内部网络,直接通过服务名访问后端容器:
修改后的Nginx配置(default.conf)
server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name example.com www.example.com; ssl_certificate /etc/nginx/certs/example.com/fullchain.pem; ssl_certificate_key /etc/nginx/certs/example.com/privkey.pem; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; location / { proxy_pass http://frontend:80; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } # 直接代理到Docker内部的backend服务,无需走外部域名 location /api/ { proxy_pass https://backend:5001/api/; proxy_set_header Host api.example.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 90; proxy_connect_timeout 90; proxy_send_timeout 90; # 跳过证书验证(后端用内部证书,Nginx无需验证) proxy_ssl_verify off; } access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; } server { listen 80; server_name api.example.com www.api.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name api.example.com; ssl_certificate /etc/nginx/certs/api.example.com/fullchain.pem; ssl_certificate_key /etc/nginx/certs/api.example.com/privkey.pem; # 同样直接访问内部backend服务 location / { proxy_pass https://backend:5001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_ssl_verify off; } error_log /var/log/nginx/api_error.log; access_log /var/log/nginx/api_access.log; }
关键修改点:
- 将
proxy_pass目标从外部域名https://api.example.com改为Docker服务名https://backend:5001(同一Docker网络下,服务名可直接解析) - 添加
proxy_ssl_verify off:避免Nginx验证后端内部证书的有效性,解决证书信任问题
2. 优化后端配置(可选,简化证书管理)
当前后端容器自己配置了HTTPS证书,而Nginx也做了HTTPS终止,可让后端仅监听HTTP端口,由Nginx统一处理HTTPS,减少证书重复挂载和配置复杂度:
修改docker-compose.yml中的backend服务配置
backend: image: example.server container_name: example.server build: context: ./example.server dockerfile: dockerfile ports: - "5000:5000" # 移除5001的端口映射,内部仅用HTTP environment: - ASPNETCORE_URLS=http://+:5000 # 改为HTTP监听 # 移除HTTPS证书相关环境变量 # 移除证书挂载 networks: - app-network
对应修改Nginx中的proxy_pass为HTTP
example.com的/api/ location改为:
location /api/ { proxy_pass http://backend:5000/api/; # 其他配置不变,去掉proxy_ssl_verify }
api.example.com的location改为:
location / { proxy_pass http://backend:5000; # 其他配置不变 }
这种方式更简洁,所有HTTPS处理都交给Nginx,后端专注业务逻辑。
3. 前端请求路径调整(可选)
如果希望前端直接请求https://api.example.com/api而非https://example.com/api,可在Vite配置中设置API基础路径:
Vite配置文件(vite.config.js/ts)
export default defineConfig({ // ...其他配置 server: { proxy: { '/api': { target: 'https://api.example.com', changeOrigin: true, secure: true } } }, build: { // 生产环境通过环境变量设置 define: { 'process.env.VITE_API_BASE_URL': JSON.stringify('https://api.example.com/api') } } })
前端代码中使用import.meta.env.VITE_API_BASE_URL作为API请求前缀即可。
4. 验证配置有效性
- 重启所有容器:
docker-compose down && docker-compose up -d --build
- 进入Nginx容器,测试内部连通性:
docker exec -it nginx sh # 测试访问后端(对应后端HTTP端口) curl http://backend:5000/api/your-endpoint # 若保留HTTPS则执行 curl https://backend:5001/api/your-endpoint -k
如果能正常返回数据,说明内部网络连通正常。
内容的提问来源于stack exchange,提问作者mixer519

