You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

配置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. 验证配置有效性

  1. 重启所有容器:
docker-compose down && docker-compose up -d --build
  1. 进入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:09:56