场景:容器内同时跑 Nginx 和 Go 后端。Nginx 负责:前端 Vue SPA 静态资源(7777 端口)、
/api反向代理到 Go(8888)、/static静态资源代理(上传的图片/文件)并带 30 天强缓存。同时解决四个经典问题:SPA 路由 404、Cookie 丢失、WebSocket 握手失败、非 root 用户运行权限。

1. 完整配置 nginx.conf
nginx
# 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
nginx
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 透传 ------ 解决登录态丢失
nginx
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 ------ 协议升级
nginx
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 用户运行 ------ 临时目录
nginx
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 上传大小限制
nginx
client_max_body_size 30M;
不设置默认只有 1M,上传壁纸/图片会报 413 Request Entity Too Large。
3. 验证与排查命令
bash
# 检查配置语法
nginx -t -c /etc/nginx/nginx.conf
# 容器内实时日志
docker logs -f ledger-service
# 手动验证各路由
curl -I http://localhost:7777/ # 前端页面
curl -I http://localhost:7777/api/v1/user/info # API 代理
curl -I http://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 的配合
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 天强缓存,后端只处理动态请求