求助:用Nginx部署Docker化React+Django应用的静态文件与反向代理
我有一个Docker化的React前端+Django后端应用,想用Nginx作为Web服务器,同时托管React和Django的静态文件,并反向代理用户请求到Gunicorn运行的Django后端。但Nginx静态文件服务的配置一直无法正常工作,已经折腾了近4个月,恳请帮忙排查。
相关配置文件
Nginx Dockerfile
FROM node:18.16-alpine as build WORKDIR /app/frontend/ COPY ../frontend/package*.json ./ COPY ../frontend ./ RUN npm install RUN npm run build # production environment FROM nginx COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/frontend/build /usr/share/nginx/html
docker-compose.yml
version: '3.9' services: db: image: mysql:8.0 ports: - '3307:3306' restart: unless-stopped env_file: - ./backend/.env volumes: - mysql-data:/var/lib/mysql backend: image: vinmartech-backend:latest restart: always build: context: ./backend dockerfile: ./Dockerfile ports: - "8000:8000" volumes: - static:/app/backend/static/ env_file: - ./backend/.env depends_on: - db nginx: build: context: . dockerfile: ./nginx/Dockerfile ports: - "80:80" volumes: - static:/app/backend/static/ restart: always depends_on: - backend # - frontend volumes: mysql-data: static:
Nginx配置文件(default.conf)
upstream mybackend { server backend:8000; } server { listen 80; location /letter/s { proxy_pass http://backend; } location /contact/ { proxy_pass http://backend; } location /admin/ { alias /static/; } location /static/ { alias /static/; } location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } }
Django静态文件配置
STATIC_URL = "/static/" STATIC_ROOT = os.path.join(BASE_DIR, 'static')
问题排查与修复方案
1. 修正Nginx静态文件路径映射
Nginx容器中静态卷的实际挂载路径是/app/backend/static/,但原配置里的alias /static/指向容器根目录下的不存在路径,导致静态文件无法加载。
修改Nginx的/static/ location:
location /static/ { alias /app/backend/static/; # 添加缓存优化规则 expires 30d; add_header Cache-Control "public, immutable"; }
2. 修复Admin路径配置
原配置中location /admin/用alias指向静态目录完全错误——Admin是Django的后端视图,需要代理到Django服务;而Admin的静态文件会被自动收集到/static/admin/,由上面的/static/规则处理。
替换原/admin/ location为代理配置:
location /admin/ { proxy_pass http://mybackend/admin/; # 必须添加这些请求头,避免Django识别请求异常 proxy_set_header Host $host; 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; }
3. 统一代理路径的斜杠规则
原配置中location /letter/s无结尾斜杠,配合proxy_pass http://backend会导致路径拼接异常(比如请求/letter/s/1会被转发到http://backend/letter/s1)。
修改代理location的路径与转发规则:
location /letter/s/ { proxy_pass http://mybackend/; proxy_set_header Host $host; 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; } location /contact/ { proxy_pass http://mybackend/; proxy_set_header Host $host; 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; }
4. 确认Django静态文件已收集
确保后端Dockerfile中执行了collectstatic命令,将静态文件同步到STATIC_ROOT(即挂载的静态卷路径):
在后端Dockerfile中添加:
# 需放在依赖安装完成后、服务启动前 RUN python manage.py collectstatic --noinput
5. 启用已定义的upstream
原配置中定义了upstream mybackend但未使用,建议统一用http://mybackend代替http://backend,方便后续扩展多后端实例。
内容的提问来源于stack exchange,提问作者Alphonse

