Nginx配置反向代理详解
反向代理是 Nginx 最常见、也是最核心的能力之一。它的作用不是直接处理业务逻辑,而是站在客户端和后端服务之间,接收请求后再转发给真正提供服务的应用,例如 Spring Boot、Node.js、Go、PHP-FPM 或其他 HTTP 服务。
很多线上系统都会把 Nginx 放在最前面,原因很直接:它能统一入口、隐藏后端服务地址、做负载均衡、处理静态资源、终止 HTTPS、控制超时与连接,并且性能稳定。
什么是反向代理
先把几个容易混淆的概念说清楚。
正向代理
正向代理服务于客户端。客户端本来无法直接访问目标服务器,于是先访问代理服务器,再由代理服务器代替客户端去访问目标服务。常见场景是科学上网、公司内网访问控制。
反向代理
反向代理服务于服务端。客户端以为自己访问的是一个统一地址,比如 https://api.example.com,实际上请求先到 Nginx,再由 Nginx 转发到后端真实服务,例如:
http://127.0.0.1:8080http://192.168.10.20:9000- 多台应用服务器组成的集群
客户端通常感知不到后端真实结构,这就是反向代理的核心特征。
为什么项目里经常使用 Nginx 反向代理
反向代理不是“为了配 Nginx 而配 Nginx”,而是解决真实问题。
统一访问入口
前端、移动端、第三方系统只需要访问一个域名,不需要知道后端服务部署在哪台机器、哪个端口。
例如:
- 用户访问
https://www.example.com - Nginx 转发到
http://127.0.0.1:8080
后端哪怕改端口、迁移机器,外部访问地址也可以不变。
隐藏后端服务
后端应用一般不直接暴露在公网。Nginx 作为入口,外部只看到 Nginx 的地址,后端服务保留在内网或本机,安全性更好。
支持负载均衡
当后端服务不止一台时,Nginx 可以把请求分发给多个节点,提高并发处理能力和可用性。
分离静态资源和动态请求
例如:
/static/由 Nginx 直接返回静态文件/api/转发到 Java 或 Node 服务
这样可以减少应用服务器压力。
支持 HTTPS 终止
浏览器与 Nginx 之间使用 HTTPS,Nginx 再通过 HTTP 转发给内网应用。后端应用不用自己处理证书,部署和维护都更简单。
反向代理最基础的配置
先看一个最小可用示例:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
这段配置的含义非常明确:
- Nginx 监听 80 端口
- 当请求域名是
example.com时进入这个server - 所有
/路径下的请求都转发到127.0.0.1:8080
例如用户访问:
http://example.com/user/list
最终会被转发到:
http://127.0.0.1:8080/user/list
这是理解 Nginx 反向代理的第一步:location 负责匹配请求,proxy_pass 负责转发请求。
一个完整些的反向代理配置应该怎么写
实际项目里,通常不会只写一行 proxy_pass,而是会补充一组代理头与超时参数。
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
这些配置分别是什么意思
proxy_set_header Host $host
把原始请求里的主机名传给后端。
很多后端应用会依赖 Host 头来:
- 生成回调地址
- 识别当前访问域名
- 做多租户或多站点处理
如果不传,后端拿到的主机名可能不是用户实际访问的域名。
proxy_set_header X-Real-IP $remote_addr
把客户端真实 IP 传给后端。否则后端看到的往往只有代理服务器的 IP。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for
记录完整的代理链路 IP。这个头在日志分析、风控、审计、限流中都很常用。
proxy_set_header X-Forwarded-Proto $scheme
告诉后端原始请求是 http 还是 https。这在应用生成跳转地址、回调地址时非常重要。
超时配置
proxy_connect_timeout:和后端建立连接的超时时间proxy_send_timeout:向后端发送请求的超时时间proxy_read_timeout:读取后端响应的超时时间
如果不根据业务特点设置,线上容易出现请求长时间挂起或者误判超时。
proxy_pass 结尾带不带斜杠,区别非常大
这是 Nginx 反向代理里最常见的坑之一。
情况一:proxy_pass 后面不带斜杠
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
用户请求:
http://example.com/api/user/info
转发结果:
http://127.0.0.1:8080/api/user/info
也就是说,请求 URI 会原样拼接过去。
情况二:proxy_pass 后面带斜杠
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}
同样的请求:
http://example.com/api/user/info
转发结果:
http://127.0.0.1:8080/user/info
这里 location 匹配到的 /api/ 前缀会被替换掉。
什么时候该带斜杠
看后端服务希望接收什么路径。
后端接口本身就以 /api 开头
例如后端接口地址就是:
http://127.0.0.1:8080/api/user/info
那就不应该带斜杠:
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
后端服务不需要 /api 前缀
例如后端接口实际是:
http://127.0.0.1:8080/user/info
只是希望对外统一暴露为 /api/,那就应该带斜杠:
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}
这个规则一定要记牢。很多 404、路径错乱问题,本质上就是这里配置错了。
按路径转发到不同服务
微服务、前后端分离项目里,经常需要按路径把请求分发到不同系统。
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /admin/ {
proxy_pass http://127.0.0.1:8090/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
}
这个配置表达的是:
/api/走后端接口服务/admin/走管理后台服务/走前端静态页面
这类配置在 Vue、React、Angular 前端项目中非常常见。
使用 upstream 管理后端节点
如果后端只有一台机器,直接写 proxy_pass http://127.0.0.1:8080; 就够了。 但只要进入正式环境,通常都建议通过 upstream 管理后端节点。
upstream backend_server {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这样写的好处是:
- 后端地址统一管理
- 后续扩容更方便
- 配置更清晰
反向代理配合负载均衡
Nginx 不只是“转发到一台后端”,还可以把请求分配到多台机器。
upstream backend_cluster {
server 192.168.10.101:8080;
server 192.168.10.102:8080;
server 192.168.10.103:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
默认情况下,Nginx 会按轮询方式分发请求。
常见负载均衡策略
1. 轮询
默认策略,每个请求依次发给不同服务器。
2. weight 权重
upstream backend_cluster {
server 192.168.10.101:8080 weight=5;
server 192.168.10.102:8080 weight=3;
server 192.168.10.103:8080 weight=2;
}
机器性能不一样时,可以让高性能机器承担更多请求。
3. ip_hash
upstream backend_cluster {
ip_hash;
server 192.168.10.101:8080;
server 192.168.10.102:8080;
}
同一个客户端 IP 会尽量落到同一台机器,适合某些依赖会话粘性的旧系统。
但要明确:现代系统更推荐把会话放到 Redis 或数据库,而不是强依赖 ip_hash。
反向代理 WebSocket 的配置
普通 HTTP 代理配置不一定能直接支持 WebSocket。 如果后端有 WebSocket 服务,需要显式配置升级头。
server {
listen 80;
server_name ws.example.com;
location /ws/ {
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 600s;
}
}
关键点有两个:
proxy_http_version 1.1Upgrade和Connection请求头
少任何一个,WebSocket 握手都可能失败。
反向代理上传文件时的注意点
默认情况下,Nginx 对请求体大小有限制。上传大文件时,如果不改配置,常见报错是 413 Request Entity Too Large。
server {
listen 80;
server_name upload.example.com;
client_max_body_size 100m;
location /upload/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
client_max_body_size 的作用
它控制客户端请求体的最大允许大小。 比如文件上传、表单提交、大 JSON 请求体,都受它影响。
这个参数应该根据业务实际需要设置,不宜无限放大。
反向代理 HTTPS 的典型配置
线上最常见的是 Nginx 终止 HTTPS,然后转发给后端 HTTP 服务。
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
location / {
proxy_pass http://127.0.0.1:8080;
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 https;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
这个配置做了两件事:
- 443 端口处理 HTTPS 请求,并代理到后端
- 80 端口全部跳转到 HTTPS
为什么要传 X-Forwarded-Proto
因为后端实际接收到的可能是来自 Nginx 的 HTTP 请求。 如果不告诉后端“用户原始请求其实是 HTTPS”,后端在生成跳转链接时可能错误地返回 HTTP 地址。
常见 location 匹配规则要理解
很多人以为 Nginx 代理问题出在 proxy_pass,实际常常是 location 匹配优先级没搞明白。
先看几种常见写法:
location = /login { ... }
location /api/ { ... }
location ^~ /static/ { ... }
location ~ \.php$ { ... }
location / { ... }
常见含义
=:精确匹配- 普通前缀匹配:按最长前缀匹配
^~:前缀匹配成功后,不再继续匹配正则~:区分大小写的正则匹配~*:不区分大小写的正则匹配
一个容易出错的例子
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}
location / {
root /usr/share/nginx/html;
index index.html;
}
这里 /api/ 会优先于 /,因为它前缀更长。 但如果又加了正则规则,就可能因为优先级变化导致请求走错位置。 所以复杂配置里,不要只盯着 proxy_pass,一定要一起检查 location 规则。
反向代理时后端获取真实 IP 的问题
只在 Nginx 配 X-Real-IP 和 X-Forwarded-For 还不够,后端框架也要正确解析这些头。
例如 Spring Boot 项目,如果没有正确启用转发头处理,程序里拿到的 IP 可能仍然是代理服务器地址。
这件事本质上分两步:
- Nginx 把真实客户端 IP 传过去
- 后端框架信任并解析这些头
如果后端拿到的还是代理 IP,不要只查 Nginx,应用框架配置也要一起看。
一份更贴近生产环境的反向代理示例
upstream app_backend {
server 192.168.10.101:8080 weight=5;
server 192.168.10.102:8080 weight=5;
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
client_max_body_size 50m;
location /api/ {
proxy_pass http://app_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_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
location /ws/ {
proxy_pass http://app_backend/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 600s;
}
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
}
这份配置已经覆盖了多数前后端分离项目的核心诉求:
- HTTP 跳 HTTPS
- 静态资源直接返回
/api/反向代理到应用集群- 支持 WebSocket
- 支持大文件上传
- 传递真实请求头
- 配置合理超时
部署和生效流程
写完配置后,不要直接重启,先检查语法。
nginx -t
如果输出类似:
syntax is ok
test is successful
说明配置语法没有问题。
然后再重新加载配置:
nginx -s reload
或者使用 systemd 的系统中:
systemctl reload nginx
这里要区分两个动作:
reload:重新加载配置,通常不中断现有连接restart:重启服务,可能影响当前请求
线上环境优先使用 reload。
常见问题排查
1. 访问出现 404
优先检查这几个点:
location是否匹配到了预期规则proxy_pass结尾斜杠是否写对- 后端接口真实路径是否和转发后的路径一致
- 后端服务是否真的启动在对应端口
很多所谓“Nginx 配错了”,实际是路径拼接规则没搞清楚。
2. 访问出现 502 Bad Gateway
502 一般表示 Nginx 连不上后端,或者后端返回异常。
常见原因:
- 后端服务没启动
- 后端监听地址不对,例如只监听了某个网卡
- 端口写错
- 后端进程崩溃
- 防火墙或安全组拦截
排查方式
先在 Nginx 所在机器上直接请求后端:
curl http://127.0.0.1:8080
如果这里都不通,就不是 Nginx 转发逻辑的问题,而是后端服务本身不可达。
3. 上传文件时报 413
这是请求体太大。 检查 client_max_body_size。
4. 后端拿不到真实域名或协议
检查这些头是否正确传递:
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
5. WebSocket 连接失败
重点检查:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
6. 前端路由刷新 404
这是单页应用很常见的问题。 例如 Vue Router、React Router 使用 history 模式后,直接刷新 /user/profile,如果 Nginx 只按物理文件查找,就会返回 404。
应使用:
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
这样不存在的物理路径会回退到 index.html,再由前端路由接管。
写反向代理配置时的几个实践建议
不要一上来就写复杂规则
先让最简单的代理打通,再逐步加:
- 请求头
- 超时
- HTTPS
- 负载均衡
- WebSocket
- 缓存与限流
复杂配置一次堆满,排错会很困难。
明确“对外路径”和“后端路径”的关系
这决定了 proxy_pass 是否带斜杠,也决定了后端接口该不该保留前缀。
日志一定要看
Nginx 排错不能只靠猜。 要结合:
- 访问日志
access.log - 错误日志
error.log
很多 502、499、504 问题,日志里都会直接给线索。
代理层和应用层要一起看
反向代理问题经常不是单点问题,而是链路问题:
- Nginx 路径转发是否正确
- 后端服务是否可达
- 应用路由是否存在
- 框架是否正确识别代理头
- 前端调用地址是否写对
只盯一个地方,往往查不出来。
总结
Nginx 配置反向代理的本质只有一句话:把客户端请求按预期规则稳定地转发给后端服务。 但真正决定配置是否可用的,不是会不会写 proxy_pass,而是你是否清楚下面这些问题:
- 请求会被哪个
location匹配 - 转发后的 URI 到底是什么
- 是否需要保留原始 Host、客户端 IP、协议头
- 后端是否需要负载均衡
- 是否涉及 HTTPS、WebSocket、文件上传、前端路由回退
把这些点理顺后,Nginx 反向代理并不复杂。难点从来不在语法,而在于对请求链路是否真正理解透彻。