场景:容器内同时跑 Nginx 和 Go 后端。Nginx 负责:前端 Vue SPA 静态资源(7777 端口)、
/api反向代理到 Go(8888)、/static静态资源代理(上传的图片/文件)并带 30 天强缓存。同时解决四个经典问题:SPA 路由 404、Cookie 丢失、WebSocket 握手失败、非 root 用户运行权限。
1. 完整配置nginx.conf
# Nginx 配置文件 # 容器以非 root 用户 (1000) 运行,不切换用户 worker_processes auto; # 自动匹配 CPU 核数 pid /run/nginx/nginx.pid; # PID 文件位置(非 root 可写) error_log /var/log/nginx/error.log warn; events { worker_connections 1024; # 单 worker 最大连接数 } http { # 非 root 用户临时目录(覆盖 Alpine 默认的 /var/lib/nginx/tmp,否则 403 权限错误) client_body_temp_path /tmp/nginx_client_body; proxy_temp_path /tmp/nginx_proxy; fastcgi_temp_path /tmp/nginx_fastcgi; uwsgi_temp_path /tmp/nginx_uwsgi; scgi_temp_path /tmp/nginx_scgi; # MIME 类型 include /etc/nginx/mime.types; default_type application/octet-stream; access_log /var/log/nginx/access.log; server { listen 7777; # 前端入口端口 server_name _; client_max_body_size 30M; # 上传文件大小限制(壁纸/图片/头像) # ───────────────────────────── # 前端静态文件 + SPA 路由 # ───────────────────────────── location / { root /usr/share/nginx/html; index index.html; # SPA 路由核心:所有未匹配的路径都返回 index.html # 由前端 vue-router 接管路由(history 模式) try_files $uri $uri/ /index.html; } # ───────────────────────────── # 后端 API 反向代理 # ───────────────────────────── location /api/ { proxy_pass http://127.0.0.1:8888; # 注意:无尾斜杠 = 保留原始 URI 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; # 确保 cookie 透传(解决 session/JWT 丢失问题) proxy_set_header Cookie $http_cookie; proxy_pass_header Set-Cookie; # 禁止 nginx 修改重定向(解决 307 二次请求问题) proxy_redirect off; # WebSocket 支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # 代理超时(聊天/SSE 流式接口需要) proxy_connect_timeout 90; proxy_read_timeout 90; } # ───────────────────────────── # 静态文件代理(壁纸、聊天文件、头像等) # ───────────────────────────── location /static/ { proxy_pass http://127.0.0.1:8888; # Go 后端 r.Static("/static", "./static") 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; # 静态资源强缓存 30 天 expires 30d; add_header Cache-Control "public, immutable"; } } }2. 逐段讲解:每个配置解决什么问题
2.1 SPA 路由 ——try_files
location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }问题:Vue Router 用 history 模式时,URL 是/blog/article/123而不是/#/blog/article/123。用户刷新页面时,Nginx 找不到对应的真实文件,返回 404。
解决:try_files依次尝试:
$uri— 命中真实文件(如/assets/index-xxx.js)$uri/— 命中目录(自动找 index.html)/index.html— 兜底,交给前端路由处理
注意:后端要有对应的 API 接口或返回 404 页面,避免 SEO 无效页面被索引。
2.2 Cookie 透传 —— 解决登录态丢失
proxy_set_header Cookie $http_cookie; proxy_pass_header Set-Cookie;问题:默认 Nginx 会透传 Cookie,但某些场景(跨域、代理到不同端口)会导致 Session 丢失。显式声明后:
Cookie:把客户端 Cookie 原样传给后端Set-Cookie:后端设置的 Cookie 原样返回给客户端
2.3 307 重定向问题 ——proxy_redirect off
问题:Go 后端某些接口返回 307(如 HTTP→HTTPS 跳转),Nginx 默认会改写 Location 头,导致客户端跳转到错误地址,出现"二次请求"或死循环。
解决:proxy_redirect off;让 Location 头原样透传。
2.4 WebSocket —— 协议升级
proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";问题:WebSocket 握手时客户端发Upgrade: websocket头,HTTP/1.0 不支持长连接,Nginx 必须用 HTTP/1.1 转发并带上 Upgrade/Connection 头,否则握手失败。
2.5 非 root 用户运行 —— 临时目录
client_body_temp_path /tmp/nginx_client_body; proxy_temp_path /tmp/nginx_proxy;问题:容器以USER 1000:1000运行时,Nginx 默认的/var/lib/nginx/tmp没有写权限,上传文件或代理大响应时直接 403/500。
解决:把临时目录全部指到/tmp下,并在 Dockerfile 里chown给用户 1000。
2.6 上传大小限制
client_max_body_size 30M;不设置默认只有 1M,上传壁纸/图片会报413 Request Entity Too Large。
3. 验证与排查命令
# 检查配置语法nginx-t-c/etc/nginx/nginx.conf# 容器内实时日志dockerlogs-fledger-service# 手动验证各路由curl-Ihttp://localhost:7777/# 前端页面curl-Ihttp://localhost:7777/api/v1/user/info# API 代理curl-Ihttp://localhost:7777/static/wallpapers/xxx.jpg# 静态资源(看 Cache-Control)4. 常见问题排查表
| 现象 | 原因 | 解决 |
|---|---|---|
| 刷新页面 404 | 没配try_files | 加try_files $uri $uri/ /index.html; |
| 登录后刷新掉登录态 | Cookie 没透传 | 加proxy_set_header Cookie+proxy_pass_header Set-Cookie |
| WebSocket 连不上 | 缺 Upgrade 头 | 加proxy_http_version 1.1+ Upgrade/Connection 头 |
| 上传 413 | 体积超限 | client_max_body_size 30M; |
| 上传 403 | 非 root 无写权限 | 临时目录指到/tmp+ chown |
| 图片不更新 | 强缓存 30 天 | 文件名带 hash 或改用no-cache |
| 接口 502 | 后端没启动/端口不对 | 检查proxy_pass地址与后端监听端口 |
5. 与 Dockerfile 的配合
# 非 root 运行所需:创建目录并授权 RUN mkdir -p /var/log/nginx /run/nginx /var/lib/nginx/logs /tmp/nginx_client_body /tmp/nginx_proxy && \ chown -R 1000:1000 /app /usr/share/nginx/html /var/log/nginx /run/nginx /var/lib/nginx /tmp/nginx_client_body /tmp/nginx_proxy USER 1000:1000总结
这份配置的核心价值:
- 一个 7777 端口搞定前后端:静态资源 + API + WebSocket + 静态文件,外部只暴露一个入口
- 四个经典坑全部规避:SPA 404、Cookie 丢失、WS 握手失败、非 root 权限
- 性能:静态资源 30 天强缓存,后端只处理动态请求