Nginx在实际生产环境中有三块能力是绕不开的,一块是反向代理,决定请求如何被接住、改造并转发到后端;一块是负载均衡,决定流量如何在后端集群之间分发;还有一块是Rewrite重写,决定请求在进入后端之前如何被拦截、改址和跳转。前两者属于流量入口层的调度与中转,后者属于配置层的精细控制。这篇文章把三部分合在一起讲透,全部用真实配置加数字推演的方式展开,让你像看剧本一样看清每个请求在Nginx内部的完整旅程。
第一部分:反向代理
场景一:让Nginx站到应用前面
很多人第一次接触Nginx时,最容易把它理解成一个静态文件服务器,但在真实生产环境里,它更常见的身份其实是反向代理。所谓反向代理,就是客户端以为自己在直接访问网站,实际上请求先到Nginx,再由Nginx代替客户端去访问真正的后端应用,比如Java、PHP、Node.js、Go或者Python服务。
最经典的场景是:前端用户访问80或443端口,Nginx接住流量,然后把动态请求转发给运行在内网端口上的应用服务器。这样做有三个直接好处,第一是隐藏后端真实地址,第二是把SSL终止、限流、缓存、日志等横切能力统一收口在入口层,第三是为后续的负载均衡和灰度发布留出操作空间。
server { listen 80; server_name www.example.com;
location / { proxy_pass http://127.0.0.1:8080; }}这段配置的含义非常直接。用户访问www.example.com时,请求先落到Nginx,location /表示所有路径默认都匹配这里,然后proxy_pass把请求转发给本机8080端口上的后端应用。浏览器看到的始终是80端口的网站地址,真正干活的是后面的业务服务。
NOTE正向代理代理的是客户端,常见于科学上网和企业出口代理;反向代理代理的是服务端,客户端通常感知不到后端真实拓扑。Nginx在Web场景里默认讨论的几乎都是反向代理。
如果想快速验证代理是否真的生效,可以在后端先启动一个简单服务,然后用curl去打Nginx入口。
# 假设后端服务监听在8080端口curl -I http://www.example.com
# 或者本机测试Host头curl -I -H "Host: www.example.com" http://127.0.0.1场景二:把用户真实信息透传给后端
只会写proxy_pass还不够,因为默认情况下,后端拿到的很多请求信息已经不是最原始的样子了。尤其是在登录、审计、鉴权、回调地址生成这些场景里,后端通常需要知道客户端真实IP、原始Host、访问协议到底是HTTP还是HTTPS。
生产环境里更推荐把常用代理头一起配上:
server { listen 80; server_name app.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; }}这四行几乎可以算反向代理的标配。
| 头部 | 作用 |
|---|---|
Host | 告诉后端用户访问的原始域名,便于多域名站点识别 |
X-Real-IP | 透传客户端真实IP,常用于日志和风控 |
X-Forwarded-For | 记录完整代理链路中的客户端IP列表 |
X-Forwarded-Proto | 告诉后端原始请求协议是HTTP还是HTTPS |
如果你不传这些头,后端框架经常会出现一串非常典型的问题:日志里全是127.0.0.1,回调地址错误地生成成http://,安全审计看不到真实来源,甚至登录态回跳都可能出错。
TIP只要你的业务需要知道“用户从哪里来、是怎么来的、访问的是哪个域名”,这几行
proxy_set_header就尽量不要省。
场景三:路径映射、静态资源与WebSocket
反向代理最容易写出事故的地方,不是proxy_pass本身,而是URI拼接规则。尤其是location和proxy_pass结尾有没有斜杠,行为差别非常大,很多线上404和接口路径错乱都出在这里。
先看一组最容易混淆的配置:
server { location /api/ { proxy_pass http://127.0.0.1:9000/; }
location /service/ { proxy_pass http://127.0.0.1:9000; }}如果用户访问/api/user/list,第一段配置会把匹配到的/api/前缀替换掉,最终转发成后端的/user/list。而访问/service/user/list时,第二段配置因为proxy_pass后面没有斜杠,Nginx会把原始URI整体拼上去,后端实际收到的是/service/user/list。
WARNING
proxy_pass末尾的斜杠不是格式问题,而是行为开关。写错时最典型的表现就是前端地址正常、后端接口却一直404。
除了动态接口,静态资源和长连接也常常交给Nginx统一处理。下面是一段非常常见的综合配置:
server { listen 80; server_name app.example.com;
location /static/ { root /data/www; expires 7d; }
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"; }
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; }}这段配置体现了反向代理最有价值的地方:同一个入口域名下,静态资源由Nginx直接返回,WebSocket请求升级后转给长连接服务,普通动态请求继续转给主应用。对用户来说只有一个站点,对运维来说入口规则却已经实现了分流治理。
反向代理与负载均衡的关系
很多初学者会把反向代理和负载均衡当成两个完全独立的能力,其实后者往往是前者的升级版。先有Nginx代替客户端去访问后端,这叫反向代理;当Nginx面对的不再是一台后端,而是一组后端,并且还要决定请求发给谁,这才进入负载均衡的范畴。
换句话说,负载均衡几乎总是建立在反向代理之上。先把“怎么转发”搞明白,再去理解“转发给谁”,学习路径就顺了。
第二部分:负载均衡
实验环境与角色设定
为了让所有推演足够具体,我们先搭建一个虚拟的电商网站后端集群,一共三台Web服务器,每台机器的性格和能力完全不同。
第一台机器IP是10.0.0.7,配置为2核4G,是一台服役多年的老机器,性能弱但还能用。第二台机器IP是10.0.0.8,配置为8核16G,是刚采购的新机器,性能强劲。第三台机器IP是10.0.0.9,配置为2核4G,平时不干活,专门用来当备胎救急。
这三台机器将贯穿负载均衡部分的全部场景,你会看到同样的三台机器在不同算法下完全不同的命运。
场景一:流量分发策略的四种姿势
默认轮询:绝对平均的傻瓜式派单
轮询是Nginx默认的派单方式,不需要任何额外配置,写上游集群就直接生效。
upstream web_pools { server 10.0.0.7:80; server 10.0.0.8:80; server 10.0.0.9:80;}假设现在有十个用户请求按顺序到达,Nginx的派发顺序是这样的:第一个请求给7号机,第二个给8号机,第三个给9号机,第四个又回到7号机,第五个给8号机,依次循环下去,绝对平均,绝不偏心。十轮下来三台机器分别拿到四个、三个、三个请求,差距不超过一个。
这个算法的优点是简单公平,缺点也极其明显,它完全不关心机器的实际能力。8号机是8核16G的性能猛兽,7号机是2核4G的老弱病残,纯轮询下两者拿到的请求量几乎一样。结果就是8号机还没开始用力,7号机的CPU已经跑满开始报警了。平均不等于均衡,这是理解负载均衡的第一课。
加权轮询:能者多劳的权重分配
解决上面的问题,就要引入weight权重参数,让每台机器按照能力比例接活。
upstream web_pools { server 10.0.0.7:80 weight=1; server 10.0.0.8:80 weight=3; server 10.0.0.9:80 weight=1;}权重的含义是相对值而不是百分比。上面配置的总权重是五,8号机占五分之三,7号机和9号机各占五分之一。再用十个请求来推演派发顺序:第一个给7号机,接着第二、三、四个连续给8号机,第五个给9号机,第六个给7号机,第七、八、九个又给8号机,第十个给9号机。
统计最终结果,7号机接了两次,8号机接了六次,9号机接了两次,比例正好是二比六比二,和权重一比三比一完全吻合。性能强的新机器承担了百分之六十的流量,老机器只承担百分之二十,这就是能者多劳。
生产环境设置权重有一条经验法则:先对每台机器做压测,拿到每秒能处理的请求数,再按比例换算成整数权重。不要凭感觉随手填数字,权重拍脑袋的结果往往是某台机器在流量高峰被悄悄压垮。
least_conn:动态感知负载的智能调度
轮询和加权轮询有一个共同的盲区,它们只认配置不认实时负载。假设8号机上有一个请求是导出大报表,要跑三十秒,而7号机上的请求都是毫秒级返回,此时加权轮询依然会死板地按比例继续派单,完全看不到8号机已经积压了一堆长请求。
least_conn即加权最少连接算法解决了这个问题。它的核心逻辑是用当前连接数除以服务器权重,得出一个负载指标,数值最小的服务器就是当前最闲的,新请求优先发给它。
| 服务器 | 权重 | 当前连接数 | 负载指标 |
|---|---|---|---|
| 服务器A | 100 | 1000 | 10 |
| 服务器B | 50 | 500 | 10 |
| 服务器C | 50 | 200 | 4 |
上面这个例子里,服务器C的负载指标是四,全场最低,所以下一个新请求会发给服务器C,而不是机械地按权重轮流。
upstream backend { least_conn; server 10.0.0.7:80 weight=1; server 10.0.0.8:80 weight=3;}这个算法特别适合请求处理时间差异大的业务,比如既有秒级的页面浏览又有分钟级的文件导出,也适合WebSocket这种长连接服务。代价是Nginx要实时维护每台机器的连接计数,多了一层计算开销,而且刚恢复上线的机器会因为连接数为零瞬间被涌入的流量冲一下,需要配合慢启动策略缓解。
ip_hash:会话保持的救命稻草
如果你的网站有登录状态,前面几种算法会集体翻车。用户登录时请求落在8号机,Session存在8号机的内存里,刷新页面时轮询把请求分给了7号机,7号机内存里什么都没有,直接提示请重新登录,用户体验瞬间崩塌。
upstream web_pools { ip_hash; server 10.0.0.7:80 weight=1; server 10.0.0.8:80 weight=3;}原理是Nginx根据客户端IP计算哈希值,只要IP不变,哈希结果就不变,同一个用户永远被分配到固定的后端机器,Session自然不会丢。
但ip_hash有两个必须知道的缺陷。第一,当某台后端宕机被摘除后,哈希取模的分母变了,大量用户的映射关系会重新计算,会话集体漂移。第二,公司局域网或运营商NAT出口下,成百上千个用户共享同一个公网IP,这些请求会全部砸向同一台机器,造成严重倾斜。更稳妥的做法是把Session放进Redis集中存储,让后端彻底无状态,这样任何分法都不会丢会话。
场景二:健康检查,自动踢掉死机节点
被动检查的时间线推演
这是Nginx开源版内置的能力,核心参数是max_fails和fail_timeout。下面用一条完整的时间线,模拟故障发生时的真实反应。
upstream web_pools { server 10.0.0.7:80 max_fails=2 fail_timeout=10s; server 10.0.0.8:80 max_fails=2 fail_timeout=10s;}假设7号机在中午十二点整突然挂掉。十二点整,用户A的请求到达,Nginx转发给7号机,7号机没有响应,请求超时,Nginx在心里记下一笔,7号机失误一次,这次用户可能会看到504超时页面。十二点整过两秒,用户B的请求到达,轮询又转到了7号机,依然没有响应,失误计数变成两次。
就在这一瞬间,max_fails等于二的条件达成,Nginx判定7号机已死,立刻把它拉黑踢出集群,拉黑时长就是fail_timeout指定的十秒。接下来的九秒里,所有请求全部发给8号机,8号机虽然压力山大,但用户访问完全正常。十秒拉黑期到期后,Nginx会试探性地给7号机发一个请求,如果7号机已经恢复并正常返回,就把它重新加回集群继续轮询,如果还没恢复,就再次累计失败次数,再次拉黑十秒,如此循环直到它活过来。
这个功能的重要性怎么强调都不过分。如果没有它,三台机器里挂掉一台,就会有三分之一的用户持续打到死机上,网站长期处于半瘫痪状态。
被动检查的三大缺陷
被动检查听起来很美,但有三个绕不开的坑。第一,探路的用户必然牺牲,故障发现依赖真实用户请求失败,总有人要当那个倒霉的探测器。第二,它只能感知连接层面的失败,如果机器端口还在监听但应用已经僵死,连接能建立但永远不返回数据,这种脑裂状态它识别不了。第三,fail_timeout期间的恢复探测是单点试探,流量恢复缺少过渡。
主动健康检查:nginx_upstream_check_module
主动检查的思路是Nginx自己定期去探测后端,不等用户请求来试错。开源社区最流行的方案是淘宝Tengine团队开发的这个模块。
它需要编译进Nginx,步骤如下:
wget http://nginx.org/download/nginx-1.26.1.tar.gzwget https://github.com/yaoweibin/nginx_upstream_check_module/archive/refs/tags/v0.4.0.tar.gztar zxf nginx-1.26.1.tar.gz && tar zxf v0.4.0.tar.gzcd nginx-1.26.1patch -p1 < ../nginx_upstream_check_module-0.4.0/check_1.20.1+.patch./configure --add-module=../nginx_upstream_check_module-0.4.0make && make install配置示例与参数含义如下:
upstream backend { server 10.0.0.7:80; server 10.0.0.8:80;
check interval=3000 rise=2 fall=3 timeout=1000 type=http; check_http_send "GET /health HTTP/1.0\r\n\r\n"; check_http_expect_alive http_2xx http_3xx;}interval等于三千表示每三秒主动探测一次,rise等于二表示连续成功两次才标记为健康,fall等于三表示连续失败三次才判定死亡,timeout等于一千是每次探测的超时时间。check_http_send指定向健康检查接口发起请求,check_http_expect_alive声明只有返回二xx或三xx状态码才算存活。后端应用需要专门实现一个health接口,内部检查数据库、缓存等依赖后返回状态,这样才能真正识别应用层的假死。
Nginx Plus的官方方案
商业版Nginx Plus内置了health_check指令,开箱即用,还支持用match块校验响应体内容,自带可视化监控面板和慢启动特性,防止刚恢复的机器被瞬间流量冲垮。
location / { proxy_pass http://backend; health_check interval=5s fails=3 passes=2 uri=/health;}配置确实优雅,但Plus需要付费订阅,这是它唯一的门槛。
企业级方案怎么选
实际生产中有五种主流路线。中小公司最常用开源Nginx加第三方检查模块,能力完整且零成本。对可用性要求不高的简单场景,只用内置被动检查也够了,省去编译的麻烦。预算充足且追求极致运维体验的团队可以直接上Nginx Plus。在Kubernetes云原生环境里,更推荐用Readiness Probe配合Ingress Controller,健康检查交给编排层,Nginx只消费健康的节点列表,实现控制与数据分离。金融级的大型架构则会在最前端用F5或云厂商LB做四层负载和健康检查,再把流量分发给后端的Nginx集群,分层各司其职。
场景三:平滑升级,更换引擎不熄火
这是运维操作里最帅的一手。假设当前跑着Nginx1.20,现在要升级到1.22,全程用户零感知。
第一步,查出当前主进程的PID,假设是1000。
ps -ef | grep nginxkill -USR2 1000kill -WINCH 1000kill -QUIT 1000第二步发送USR2信号,这是最神奇的一步。老进程收到信号后,会拉起一个运行新版本的新主进程,假设PID是2000,此后新旧两个主进程同时监听同一个端口,老进程不再接受新连接但继续处理存量连接,正在下载文件和正在支付的用户完全不受影响,所有新进来的用户则由新进程接管。
第三步发送WINCH信号,通知老进程优雅地关闭它的工作进程。工作进程不会立刻死掉,而是把手上剩余的旧连接处理完再退出,做到有始有终。
第四步发送QUIT信号,等旧连接全部处理完毕,老主进程彻底消失,舞台上只剩下新版本的2000号进程。
整个过程中没有任何一个用户的连接被掐断,页面没有任何卡顿和报错,这就是平滑二字的真正含义。另外务必保留旧版本的二进制文件,万一新版本出现异常,可以向新进程发HUP、向老进程发USR2快速回退,给自己留好退路。
工业级综合配置
把前面三个场景串起来,就是一套可以直接上生产的完整配置。
upstream web_pools { ip_hash;
server 10.0.0.7:80 weight=1 max_fails=3 fail_timeout=30s; server 10.0.0.8:80 weight=3 max_fails=3 fail_timeout=30s; server 10.0.0.9:80 backup;}
server { listen 80; location / { proxy_pass http://web_pools; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}这段配置里,ip_hash负责会话保持,weight负责按能力分配流量,max_fails和fail_timeout负责故障自愈,9号机后面的backup关键字是点睛之笔,表示它平时不接收任何流量,只有当所有主节点全部宕机时才会被唤醒顶上来,是真正的最后防线。下面的两行proxy_set_header负责把客户端真实IP透传给后端,否则后端日志里看到的全部是Nginx自己的IP,排查问题时会两眼一抹黑。
第三部分:Rewrite重写
概述:Nginx最像编程语言的地方
Rewrite是Nginx最像编程语言的模块,也是面试和工作中最容易写错导致死循环的重灾区。它的核心目标是当请求到达Nginx时,Nginx有权不转发请求,而是直接在自身层面完成拦截、改地址、贴标签和重定向四件事。
四个指令的关系可以用一句话记住:return是拦路虎直接给结果,if是条件分支做判断,set是贴标签定义变量,rewrite是改地址做重写。一个请求进入Nginx后,会先经过set定义变量,再经过if做条件判断,然后由rewrite改写路径,最后随时可能被return一脚踢回给浏览器。
return指令:简单粗暴的拦截与跳转
案例:禁封后台路径
业务场景是后台管理页面在admin路径下,不想让外网访问,直接在Nginx层面拦截。
location /admin/ { return 403;}当用户访问后台地址时,Nginx的内心独白是这样的:用户想进后台,我看了一下location规则,匹配到了admin路径,那我就不去找后端服务器了,直接甩回一个403禁止访问。整个过程中后端PHP或Java服务器连日志都不会记录这个请求,因为流量根本没有转发过去,攻击面在入口就被切掉了。
案例:域名永久迁移
公司把旧域名换成了新域名,需要让访问旧域名的用户自动跳过去。
server { listen 80; server_name www.old.com; return 301 http://www.new.com$request_uri;}用户访问旧域名下的任意页面时,Nginx返回301状态码并在响应头里写上新地址,浏览器收到后自动发起新请求。这里的request_uri变量保留了完整的路径和参数,用户原来访问的商品页会精确跳到新域名的同一个商品页,而不是粗暴地全部跳到首页。选择301而不是302还有一个SEO层面的原因,301会告诉搜索引擎把旧域名的权重永久转移到新域名上。
案例:强制跳转HTTPS
现在所有正规网站都强制使用HTTPS,用户如果输入了http开头的地址,必须无缝跳转到加密通道。
server { listen 80; server_name www.oldboy.com; return 301 https://$server_name$request_uri;}server_name变量代表当前域名,request_uri变量代表带参数的完整路径,两个变量拼起来保证了用户从http过来之后路径和参数一个都不丢,这是目前公认最标准的HTTPS跳转写法。
if指令:有原则的挑客
if主要在location块里对内置变量做逻辑判断。先看一个限制请求方法的实战案例,假设网站是纯新闻展示页,只允许浏览和提交表单,其余方法一律拒绝。
location / { if ($request_method !~ ^(GET|POST|HEAD)$) { return 403; } proxy_pass http://web_pools;}黑客用DELETE方法发起攻击时,Nginx的内心独白是:收到一个DELETE请求,检查if条件,请求方法不匹配白名单正则,直接触发403拒绝,根本不转发给后端,后端服务器毫发无损。
必须提醒的是,官方文档里有一篇著名的文章标题直译过来就是if是邪恶的。Nginx的if在location上下文中的实现有历史遗留的坑,嵌套使用和配合proxy_pass时容易产生诡异行为。安全的使用姿势是让if块里只放return或rewrite这类终结性指令,不要在if里做复杂的逻辑编排。
set指令:给请求贴标签
set用来定义自定义变量,配合if可以实现业务开关。最典型的场景是网站一键进入维护模式,凌晨升级数据库时不想让用户看到报错白屏,而是看到友好的维护提示。
server { listen 80; server_name www.oldboy.com; set $maintain 1;
location / { if ($maintain = 1) { return 503; } proxy_pass http://web_pools; }}运维的操作节奏是这样的:白天正常运行时变量设为零,网站正常访问;凌晨要升级时把变量改为一,执行reload后所有用户看到503;升级完毕再改回零,网站瞬间恢复。全程不需要改动任何后端代码,纯Nginx层面就完成了停服通知。
还可以更进一步,配合error_page指令把冰冷的503状态码指向一个精心设计的静态维护页面,让用户看到品牌化的提示而不是浏览器默认报错,细节体验立刻拉开差距。
rewrite指令:最灵活的地址改写
rewrite比return强大,因为它支持正则表达式,可以捕获并改写URL中的特定部分。语法结构是rewrite加正则加替换目标加标记四段式。
案例:URL伪静态
旧页面是带问号的动态地址,为了SEO优化想改成伪静态的目录式地址。
location / { rewrite ^/user/(\d+)\.html$ /user.php?id=$1 last; proxy_pass http://web_pools;}用户在浏览器地址栏输入user斜杠888点html,Nginx收到后通过正则捕获到数字888,在内部把路径改写成user点php问号id等于888,然后带着新路径重新匹配location并转发给后端。整个过程中用户地址栏显示的依然是漂亮的静态地址,而后端PHP收到的是它能理解的动态参数,搜索引擎和用户两边都满意。
案例:用rewrite跳转HTTPS
虽然跳转HTTPS推荐用return,但老教程里常见rewrite写法,你也要看得懂。
rewrite ^(.*)$ https://$server_name$1 permanent;这里的permanent就是301永久重定向标志,效果和return301一致,只是return的执行效率更高,因为rewrite要走一遍正则引擎。
四种标记:面试必问的王炸难点
rewrite语句末尾的flag决定了改写之后的行为,这是整个模块最容易混淆的部分。用同一组配置对比四种标记的区别。
location / { rewrite ^/a/(.*) /b/$1 flag; rewrite ^/b/(.*) /c/$1 flag; proxy_pass http://web_pools;}location /b/ { return 999;}| 标记 | 执行逻辑 | 最终结果 |
|---|---|---|
| last | 改写后停止当前rewrite阶段,拿新路径重新匹配所有location | 请求变成b路径后重新匹配到b的location,返回999 |
| break | 改写后停止rewrite处理,留在当前location用新路径继续 | 直接用b路径转发给后端,不再重新匹配 |
| redirect | 返回302临时重定向,浏览器地址栏变化 | 浏览器带着新地址重新发起请求 |
| permanent | 返回301永久重定向,浏览器地址栏变化并被缓存 | 下次访问浏览器直接本地跳转,不经过Nginx |
逐个推演一遍。请求a路径进来,如果标记是last,路径被改成b之后,Nginx会拿着b路径重新走一遍location匹配流程,发现命中b的location,于是执行里面的return999,用户最终收到999。如果标记是break,路径改成b之后rewrite模块就此打住,不再执行后面的rewrite指令也不重新匹配location,直接拿着b路径在当前location里执行proxy_pass,后端收到的是b路径的请求。
redirect和permanent的共同点是都会让浏览器地址栏变化,区别在缓存行为。permanent返回的301会被浏览器永久缓存,用户下次再访问旧地址时,浏览器甚至不联系服务器,直接翻本地缓存自己跳过去。这也意味着permanent一旦配错影响很难收回,测试阶段务必先用redirect验证,确认无误后再切换成permanent。
记忆last和break的区别有一个口诀:last是重新来过,break是到此为止。last会开启新一轮location匹配,break则在当前位置画上句号。
终极排雷:死循环是怎么产生的
写rewrite时最惨烈的翻车是下面这种写法。
location / { rewrite ^/(.*) /index.php?path=$1 last; proxy_pass http://web_pools;}灾难的原理是:请求进来被改写成index点php,因为标记是last,Nginx拿着新路径重新匹配location,结果index点php依然匹配斜杠这个location,于是又被改写成index点php,无限循环。Nginx的内部重定向次数上限是十次,超过之后直接报500内部错误,日志里留下一句重定向次数过多的报错。
正确的解法有三种。最简单的是把last改成break,改写一次就停手不再重新匹配。或者让重写目标指向一个专门的location,确保不会再次命中自己。还可以在rewrite之前用if判断当前路径是否已经是目标路径,是就跳过。无论用哪种,写完rewrite之后的第一件事永远是用curl实际打一遍,确认没有循环再上线。
总结
如果把整篇文章压缩成一句话,那就是:反向代理解决“请求先由谁接住、怎么转发”,负载均衡解决“请求应该转给哪一台后端”,Rewrite解决“请求在转发前要不要改写、拦截或跳转”。这三块能力拼起来,才是Nginx在生产环境里的完整形态。
反向代理部分帮你建立入口层思维,知道为什么要透传真实头部、为什么proxy_pass的斜杠会影响URI、为什么静态资源和WebSocket也适合交给Nginx统一治理;负载均衡部分把流量调度、故障自愈和平滑升级串成一条完整链路;Rewrite部分则负责请求改写和访问控制的精细操作。把这些知识点全部吃进肌肉记忆,你在面试里被问到Nginx时就能从入口层一路讲到转发层和改写层,在工作中也能写出既优雅又安全的配置。