反向代理解决什么
Spring Boot 应用直接对外暴露 8080 端口,能跑,但不够用:要配 HTTPS、要做多实例负载均衡、要隐藏后端地址、要统一处理跨域和超时。这些事情都堆到应用里做,代码会越来越重,而且换个语言就得重写一遍。
Nginx 放在最前面,把 TLS 终止、静态资源、请求转发、限流、压缩这些"基础设施"的活接过去,后端只处理业务。反向代理最典型的用法就是:浏览器访问 api.example.com,Nginx 把请求转给内网的 8080 和 8081,用户完全感觉不到后端有几台机器。
一个可用的配置
upstream order_backend {
server 127.0.0.1:8080 weight=1 max_fails=3 fail_timeout=10s;
server 127.0.0.1:8081 weight=1 max_fails=3 fail_timeout=10s;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
client_max_body_size 20m;
location /api/ {
proxy_pass http://order_backend/;
proxy_http_version 1.1;
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;
proxy_set_header Connection "";
proxy_connect_timeout 3s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
}
几个关键点:
upstream定义后端列表,默认是轮询,weight调整权重,max_fails和fail_timeout实现被动的失败摘除;keepalive 32让 Nginx 和后端保持长连接,必须配合proxy_http_version 1.1和Connection ""才生效;proxy_set_header Host $host保留原始域名,后端生成重定向链接时不会指到内网地址;X-Real-IP和X-Forwarded-For让后端拿到真实客户端 IP,否则日志里全是 Nginx 的地址;X-Forwarded-Proto告诉后端原始请求是 HTTP 还是 HTTPS,避免重定向循环。
负载均衡策略不止轮询一种:ip_hash 让同一客户端固定落到同一台后端,适合还在用本地会话的应用;least_conn 优先转发给连接数最少的节点,适合长连接或者各接口耗时差异大的场景。被动健康检查靠 max_fails 和 fail_timeout,后端连续失败达到次数就先摘除,过一段时间再尝试恢复。它不如主动探测灵敏,但对大多数业务已经够用。
proxy_pass 的斜杠问题
这是反向代理最容易踩的坑。location 和 proxy_pass 末尾的斜杠组合不同,转发后的路径完全不同:
| 配置 | 请求 /api/user/1 转发到 |
|---|---|
location /api/ { proxy_pass http://backend/; } |
/user/1 |
location /api/ { proxy_pass http://backend; } |
/api/user/1 |
location /api { proxy_pass http://backend/; } |
/user/1(前缀被替换) |
规则可以记成:proxy_pass 带 URI(包括单独一个 /)时,会用这个 URI 替换掉 location 匹配到的前缀;不带 URI 时,原样转发完整路径。
后端接口如果本来就带 /api 前缀,用第二种;如果后端没有前缀,用第一种。两者混用时,最常见的结果是 404 或者路径里出现 //。
另外 location 的匹配顺序也值得记:= 精确匹配优先级最高,其次是 ^~ 前缀匹配(命中后不再检查正则),然后是正则 ~,最后是普通前缀匹配。写多个 location 时,别假设它们按书写顺序生效。
常见坑
1. 后端拿到的 Host 不对
不设置 proxy_set_header Host $host 时,后端收到的是 upstream 里的地址,生成的下载链接、重定向地址可能指向 127.0.0.1:8080。用户点一下就跳到了本机。
2. 上传大文件报 413
Nginx 默认 client_max_body_size 1m,上传接口超过 1MB 直接返回 413,请求根本到不了后端。在 server 或 location 里调大,并确认后端和网关的限制也放开了。
3. 502 Bad Gateway
含义是 Nginx 连不上后端。常见原因:后端没启动、端口写错、upstream 里的地址解析失败、后端进程崩溃、SELinux 阻止了 Nginx 发起网络连接。先 ss -lntp | grep 8080 确认后端在监听,再用 curl 直连后端排除应用问题。
4. 504 Gateway Timeout
Nginx 连上了后端,但等响应超时。proxy_read_timeout 默认 60 秒,后端接口跑得更久就会 504。不要一上来就把超时调到 600 秒,先确认接口为什么慢;确实是长任务,就改成异步加轮询。
5. WebSocket 连不上
WebSocket 需要额外的头:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
location /ws/ {
proxy_pass http://order_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
没有 Upgrade 和 Connection,握手会失败,前端看到的是连接被断开。
6. upstream 里的域名只解析一次
server backend.internal:8080; 这种写法在 Nginx 启动时解析一次域名,后端 IP 变了不会自动更新。云环境里要用 resolver 配合变量,或者直接写 IP。
7. 改了配置没生效
nginx -s reload 之前先 nginx -t 检查语法。配置没有 include 到主配置、改的是另一个环境的文件,也是常见原因。
排查步骤
按"配置—连通性—应用"的顺序查:
nginx -t # 语法检查
tail -f /var/log/nginx/error.log # 错误日志
curl -I http://127.0.0.1:8080/api/health # 绕过 Nginx 直连后端
curl -H "Host: api.example.com" http://127.0.0.1/api/health
ss -lntp | grep -E '80|8080'
如果直连后端正常、经过 Nginx 异常,问题在代理配置或网络;如果直连也异常,先排查应用。在 access.log 里加上 $upstream_response_time 和 $upstream_status,能直接看出是后端慢还是后端报错。
评论