☰
Nginx反向代理从原理到实战:核心配置与调优指南
2026/9/28 14:59:04 网站建设 项目流程

1. 项目背景与核心需求拆解

做 Web 开发和服务器运维的,基本都绕不开 Nginx 这个名字。无论是给前端静态页面找个宿主,还是给后端 Java、Python、Node 服务做一层转发网关,Nginx 都是最趁手的那把刀。我自己从最早的 Apache 换到 Nginx,再从单纯托管静态文件,一步步玩转反向代理、负载均衡、灰度发布,可以说 Nginx 反向代理这一块,是每一个互联网从业者都该吃透的基本功。

很多人第一次接触“反向代理”这四个字,容易被绕进去。用大白话讲:反向代理就是 Nginx 站在用户和真实服务器中间,用户其实是在跟 Nginx 打交道,真正干活的服务器躲在 Nginx 背后。用户请求过来,Nginx 判断该往哪台服务器转,再把服务器的响应原路返回给用户。整个过程中用户只认 Nginx 这一个“门面”,根本不知道背后有几台服务器、服务器是什么语言写的、目录结构长什么样。反过来,真实的业务服务器也不直接面对外网流量,因此少了很多安全上的麻烦。

作为一个多年一直在用 Nginx 的从业者,我想在这篇内容里把反向代理从原理、配置、参数调优到实战坑点完整串一遍,尤其针对搜“nginx 反向代理”时最常见的一些高频场景——比如部署前后端分离项目、给 Java 服务做网关、多域名共用一个 Nginx、WebSocket 长连接代理、以及 HTTPS 证书配置——做一个偏工程向的实操拆解。这篇文章适合刚接触 Nginx 的开发者和运维新手,也适合那些已经在用基础转发、但想搞清楚 proxy_pass 路径拼接、负载均衡策略、head 里面那些 header 到底要不要带的人。

2. Nginx 反向代理的整体设计与选型思路

2.1 为什么要用 Nginx 做反向代理,而不是别的方案

先回应一个很自然的问题:现在微服务网关有 Spring Cloud Gateway、有 Kong、有 Traefik,为什么还要学 Nginx?我的答案是——Nginx 才是那个什么时候都跑不掉的底座。

Nginx 的高性能是出了名的。在高并发场景下,Nginx 采用异步非阻塞的事件驱动模型,一个 worker 进程可以同时处理海量连接,对于大多数中小型业务来说,Nginx 作为流量入口完全扛得住。而且 Nginx 的定位非常清晰:它就是做代理和静态资源服务的,不会像业务服务一样被复杂的业务逻辑拖累。一个纯转发层,稳定性必然是第一位的。

从部署成本看,Nginx 的安装和配置极其轻量,编译出一个可执行文件不过几 MB 级别。相比 Java 系的网关动辄要引入全家桶依赖、还要考虑 JVM 内存、配置中心、注册中心,Nginx 的落地成本低到可以忽略。很多公司即便上了微服务网关,也依然习惯在最外层挂一个 Nginx 做 SSL 终结、做静态资源边缘缓存、做基础的流量分发,网关只负责内部的服务路由逻辑。

还有一个很现实的原因:Nginx 的生态兼容性太好了。Linux、Windows、macOS 都能用;和前后端分离项目的配合是天作之合;对 HTTPS 证书的支持干净利落;和 Docker、Kubernetes 里的 Ingress Controller 也有直接血缘关系。学会了 Nginx 反向代理,等于同时掌握了云原生网关的底层逻辑,这笔投入绝对不亏。

2.2 反向代理的工作模型与核心流程

从请求链路看,反向代理的角色可以拆成四个环节:接收、识别、转发、回传。

用户发起一个 HTTP 请求到 Nginx 监听的端口,Nginx 根据请求的 host 头、URL 路径、甚至请求头里的某些字段来决定将该请求交给哪个上游服务器处理。这个“决定”的过程,就是 Nginx 配置里面层层匹配的规则。匹配完成后,Nginx 建立到上游服务器的连接,把请求原样转发过去。上游处理完返回响应,Nginx 再把响应传回给客户端。注意这个过程中,Nginx 可以选择缓冲上游响应,也可以选择边收边发(proxy_buffering off),这在后面讲实时性场景时会用到。

对比正向代理,反向代理最大的区别在于代理对象不同。正向代理是替客户端去访问外部资源,客户端知道目标在哪,但目标不知道真实客户端是谁;反向代理则是替服务器接收流量,客户端知道入口在哪,但不知道真实的服务器在哪。理解了这两者的差异,就能明白为什么反向代理天生适合做负载均衡和统一入口。

2.3 为什么优先推荐源码编译安装

不少新手图省事直接用yum install nginx或者apt install nginx,这当然可以跑,但我个人在需要深度定制、需要连接最新版本模块、或者要处理特殊优化参数时,更推荐源码编译安装。编译安装的好处是你可以自己决定开启哪些模块,比如--with-http_ssl_module、--with-http_realip_module、--with-http_stub_status_module等,还能加上 C 编译器的优化参数,比如-O2,让运行效率更高。

不过话说回来,如果你的内核和系统源里带的 Nginx 版本不算太老,也没有模块需求,直接用包管理器装是最省事的。判断方式很简单:跑nginx -V看看编译参数,如果带--with-http_ssl_module和--with-http_v2_module,那日常反向代理基本够用。等需要加模块了,再走一遍编译安装或者平滑加载动态模块也不迟。

3. 核心配置拆解:从 location 匹配到 proxy_pass 的路径学问

3.1 location 匹配规则的优先级

配置 Nginx 反向代理,最核心的就是 location 块。location决定了哪些 URL 会被哪些规则接住,再接住之后怎么处理。Nginx 的 location 匹配有一套优先级体系,网上很多教程讲得云里雾里,我用一句话概括:精确匹配 > 前缀最长匹配 > 正则匹配(按书写顺序) > 普通前缀匹配。

具体规则拆开看:

  • location = /api是精确匹配,只匹配/api这一个路径,优先级最高。
  • location ^~ /static/是前缀匹配,一旦命中就不再继续检查正则,优先级仅次于精确匹配。
  • location ~ /api/和location ~* /api/是正则匹配,~区分大小写,~*不区分。
  • location /api/是普通前缀匹配,如果多个普通前缀都匹配上,取最长的那一个。

最容易踩的坑是正则匹配和无修饰符前缀匹配的优先级问题。我见过不少人在同一个 server 里既写了location ~ \.php$又写了location /api,结果/api/index.php这样的请求被 PHP 正则接走了,后端直接给你返回 404。原因就是正则匹配的优先级高于普通前缀匹配。如果你想强制某个前缀路径不被正则抢走,得用^~。

3.2 proxy_pass 带不带斜杠,结果天差地别

location 匹配规则清楚了,下一个高频出错点就是 proxy_pass 的 URL 末尾是否带斜杠。这个问题,面试考过,实战也坑过无数人。

先记住两条最基础的结论:

  • location /api/配合proxy_pass http://backend;,不带路径的部分,转发时会保留原始 URI,也就是/api/xxx整个路径都被传过去。
  • location /api/配合proxy_pass http://backend/;,带斜杠,会替换匹配到的 location 前缀。比如客户端请求/api/user/list,转发给后端的实际路径是/user/list。

为什么会有这个区别?因为 Nginx 拼接转发路径的逻辑是:如果 proxy_pass 的 URL 中不带 URI(即协议+主机+端口后直接结束),那就把客户端完整的原始请求 URI 追加到后面;如果 proxy_pass 带了 URI(哪怕只有一个/),那么匹配 location 的那一段前缀会被替换成 proxy_pass 里写的 URI 部分。

这个特性在实际项目中非常重要。很多前后端分离的项目,前端请求/api开头的接口,后端服务的 context-path 是/,那你的 location 就得写成/api/配合不带的 proxy_pass,才能保证后端收到的还是/api/xxx,后端如果配置了全局前缀匹配也能正常处理。反过来,如果后端接口本来就包含/api前缀但服务里写死了路径,你可能需要把 API 前缀剥掉再往后端转,那就要在 proxy_pass 末尾加/。

3.3 反向代理必须带上的 header 三件套

只做最简单的 proxy_pass 转发,很多场景下接口能通,但拿到线上就各种问题——最典型的就是用户真实 IP 丢失、Host 信息错误、以及 HTTPS 跳转无限循环。这一节我把必须手动配置的 header 三件套拉出来,每一个都知道为什么要加。

第一个是Host。Nginx 默认转发时,传给后端的 Host 头是 proxy_pass 里配置的 IP 或域名,而不是用户原始请求中的域名。如果你的后端服务绑定了域名白名单,或者根据 Host 做多租户判断,那就必须显式带上proxy_set_header Host $host;。用了这个配置,后端看到的 Host 就是用户访问时的域名。

第二个是X-Real-IP,带上真实客户端 IP。不加这个 header,后端所有请求日志里的 IP 全是 Nginx 的 IP,查问题、做限流、做风控全废。配置方式是proxy_set_header X-Real-IP $remote_addr;。如果你的项目里还需要完整的链路 IP 信息,或者 Nginx 前面还有一层负载均衡,那就要用X-Forwarded-For,格式类似于每次经过一层代理就追加上一层看到的客户端地址:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

第三个是X-Forwarded-Proto。当 Nginx 作为 SSL 终结节点,后端是 HTTP 时,后端服务需要知道用户原本是用 HTTPS 还是 HTTP 访问的。尤其要注意,很多应用在生成绝对路径的跳转链接或者 OAuth 回调地址时,会读这个头。如果不加,后端以为自己是 HTTP 服务,就会生成http://的链接,导致浏览器报“不安全”或跳转丢失。配置:

proxy_set_header X-Forwarded-Proto $scheme;

4. 反向代理实战:多场景配置与核心参数调优

4.1 场景一:前后端分离项目的一站式反向代理

现在的主流 Web 项目基本都是前后端分离:前端静态文件由 Nginx 直接托管,后端 API 通过反向代理转发到 Java 的 Tomcat、Spring Boot 内嵌服务、或者 Python 的 Gunicorn 上。

一个典型配置长这样:

server { listen 80; server_name www.example.com; # 前端静态资源直接由 Nginx 托管 location / { root /data/www/dist; index index.html; try_files $uri $uri/ /index.html; } # 后端 API 反向代理 location /api/ { 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; } }

try_files $uri $uri/ /index.html;这行是给 Vue、React 这类单页应用用的。前端路由由 JS 控制,服务端没有对应的物理文件路径,所以当用户直接刷新/user/profile时,Nginx 找不到这个路径,就回退到根目录的index.html,让前端框架自己接管路由。这一行如果不写,刷新页面就会得到 404。我见过无数同事在部署前端项目时卡在这一步,后来形成条件反射了,看到单页应用,脑子里自动跳出这行配置。

4.2 场景二:WebSocket 长连接代理

如果后端服务用到了 WebSocket,比如在线聊天、消息推送、协同编辑,Nginx 默认配置是转发不了的。WebSocket 协议升级需要 HTTP 请求头里的Upgrade和Connection字段,而 Nginx 默认会忽略这两个头的转发。所以必须单独给 WebSocket 的 location 写一套配置:

location /ws/ { proxy_pass http://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-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }

Connection "upgrade"是固定的写法,用于告知上游这个连接要升级成 WebSocket 长连接。proxy_http_version 1.1也很重要,WebSocket 握手依赖 HTTP/1.1 的持久连接特性。

还有一个细节是超时时间。WebSocket 连接的特点是长时间不通信,如果 Nginx 默认的proxy_read_timeout是 60 秒,那么 60 秒内客户端没有发送任何消息,Nginx 就会主动断开连接。所以要对 WebSocket 的 location 单独设置较大的超时时间,这个时间最好跟后端心跳机制配合,比如心跳是 30 秒一次,超时设 3600 秒就非常稳妥。

4.3 场景三:代理外部 HTTPS 接口(如 OpenAI 类服务)

现在很多开发者习惯用 Nginx 代理外部 API 服务,典型的就是把某个海外接口基于合规的访问需求通过一个统一网关转发出去。这类场景要处理的核心是 HTTPS 证书信任和 SNI 问题。

server { listen 80; server_name api.example.com; location / { proxy_pass https://api.someprovider.com; proxy_ssl_server_name on; proxy_set_header Host api.someprovider.com; proxy_set_header X-Real-IP $remote_addr; proxy_ssl_protocols TLSv1.2 TLSv1.3; } }

proxy_ssl_server_name on的作用是让 Nginx 在向上游发起 TLS 握手时携带 SNI(Server Name Indication)字段,也就是告诉上游服务器“我要访问的域名是哪个”。如果不加,某些 CDN 或云服务商的多域名共用一个 IP 时,会直接拒绝连接。

另外,如果你和后端服务之间经过自签证书或非标准证书链路,可能需要设置proxy_ssl_verify off,但强烈不建议在生产环境这么做,这会放弃 TLS 链路验证。安全底线还是得有。

4.4 场景四:一个 Nginx 部署多个网站(主域名 + 二级域名)

通过 Nginx 的server_name做多域名区分,是最常见的虚拟主机玩法。比如主域名 www.example.com 挂官网,二级域名 admin.example.com 挂管理后台,api.example.com 挂接口服务。只需要在一个 http 块里配置多个 server 块即可。

server { listen 80; server_name www.example.com example.com; root /data/www/site; } server { listen 80; server_name admin.example.com; location / { proxy_pass http://127.0.0.1:8081; } } server { listen 80; server_name api.example.com; location / { proxy_pass http://127.0.0.1:8082; } }

这里要特别注意server_name的匹配优先级:精确匹配 > 通配符前缀匹配(*.example.com)> 通配符后缀匹配(example.*)> 正则匹配。如果同一个请求匹配到多个 server,Nginx 会按这个优先级顺序选择。因为这种特性,有时候你会在配置里写一个default_server来兜底所有没匹配上的请求。比如:

listen 80 default_server;

配了 default_server 的那个 server 会成为默认接收者,所有未命中的请求都会进到这个配置里。这样能避免有人直接拿 IP 访问你的服务器时,意外访问到某个业务页面。

4.5 场景五:HTTPS 证书配置与强制跳转

给 Nginx 配 HTTPS 说难不难,关键是证书链完整性和重定向逻辑。常规套路:

server { listen 443 ssl; http2 on; server_name www.example.com; ssl_certificate /etc/nginx/ssl/example.com.pem; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; keepalive_timeout 70; 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; } } server { listen 80; server_name www.example.com; return 301 https://$host$request_uri; }

常见问题有两个。第一个是证书链不完整,浏览器提示“证书不受信任”。解决方法是把证书文件拆开看,确保证书内容从站点证书开始,中间证书、根证书按顺序拼在一起。很多证书提供商会直接给一个 fullchain.pem,把这个文件配给ssl_certificate就行。第二个问题是在配置强跳转 80 到 443 后,发现后端接口返回的连接还是 HTTP,多半是X-Forwarded-Proto没配,或者 Spring 这类框架对 forwarded 头理解不到位,需要额外配置 forward-headers。这一点前面已经提过,但还是要强调:X-Forwarded-Proto在 HTTPS 场景下真的能救命。

4.6 负载均衡与健康检查配置

反向代理最常见的进阶玩法就是负载均衡。Nginx 通过upstream块定义一组后端服务器,然后 proxy_pass 指向这个 upstream 名称。

upstream backend_cluster { least_conn; server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s; server 192.168.1.11:8080 weight=1 backup; keepalive 32; } server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_cluster; proxy_set_header Host $host; } }

这里的策略配置非常实用:

  • 默认是轮询(round-robin),每个请求按顺序轮流分发。
  • least_conn会将新请求发给当前活跃连接数最少的后端,适合处理长请求、慢接口不均匀的场景。
  • weight用来做权重分配,比如新机器刚上线,可以暂时给低权重,观察稳定性后再调高。
  • backup标记的服务器只在所有非备份节点不可用时才会被启用,适合做冷备。
  • max_fails和fail_timeout控制健康检查逻辑。比如max_fails=3 fail_timeout=30s表示后端连续失败 3 次后,Nginx 在 30 秒内不再往这个节点发请求。

很多人容易忽略keepalive的作用。默认 Nginx 向后端发请求是短连接,每次都要重新建 HTTP 连接,性能和延迟都有损耗。配了keepalive 32之后,Nginx 会和上游服务器保持一批空闲长连接,复用连接转发请求。尤其在高 QPS 场景下,这个优化效果非常明显。要启用这个特性,还必须配proxy_http_version 1.1;和proxy_set_header Connection "";,否则 keepalive 连接池不会生效。

4.7 缓存、缓冲与超时参数调优

反向代理不只是转发,几个高频调优点直接影响用户体验和服务器稳定性。

proxy_buffering默认是 on,意味着 Nginx 会先把后端的响应全部收完,再统一返回给客户端。这么做的好处是后端响应慢时,Nginx 不会把慢速客户端和后端服务直接绑死,而是自己做一个缓冲,降低后端的并发压力。坏处是像 Server-Sent Events、流式输出这类场景,客户端希望数据一到就立刻看到,缓冲反而破坏了实时性。所以流式场景要显式关闭:

proxy_buffering off;

proxy_buffer_size控制 Nginx 接收后端响应头部的缓冲区大小,默认一般是 4k 或 8k。如果后端返回的 Cookie 很长、响应头特别大,而缓冲区不够,Nginx 会返回 502 或者“upstream sent too big header”错误。常见解法是调大:

proxy_buffer_size 16k; proxy_buffers 8 16k;

proxy_connect_timeout默认 60 秒,定义 Nginx 与后端建立 TCP 连接的超时时间。内网服务一般 5 秒到 10 秒足够;公网链路不稳定时可以放宽到 30 秒。

proxy_read_timeout默认也是 60 秒,定义后端处理完并返回数据的最大等待时间。业务接口偶尔会有长耗时,比如导出报表跑 3 分钟,那就必须把proxy_read_timeout调到 300 秒以上,否则 Nginx 会在 60 秒后强断连接。这类问题出现时,后端日志里没任何报错,但客户端看到 504,原因就在这里。

5. 安全加固与权限边界

5.1 隐藏版本号与基础加固

Nginx 默认会在 HTTP 响应头里带上Server: nginx/1.18.0这样的版本信息。攻击者可以据此搜索对应版本的历史漏洞。隐藏版本号的办法:

server_tokens off;

这只是一个态度问题,真正的加固还要从访问控制和请求体限制做起。下面这段配置属于基础安全基线:

# 限制单个请求体大小,防止大文件上传拖垮后端 client_max_body_size 10m; # 拒绝非法的 HTTP 方法 if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) { return 444; } # 限制浏览器访问某些敏感路径 location ~ /\.(git|svn|env|htaccess) { deny all; }

return 444是 Nginx 的非标准返回码,收到这个指令后 Nginx 直接关闭连接,不给任何响应内容,比返回 403 更节省资源,也是很多安全加固方案的常规操作。

5.2 反向代理层防御常见攻击

如果你是用 Nginx 代理到公网服务,或者代理层本身暴露在公网,建议至少考虑两层防护。

第一层是限流。Nginx 自带的limit_req模块可以做请求速率限制。比如限制每个 IP 每秒只能请求 10 次:

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; location /api/ { limit_req zone=api_limit burst=20 nodelay; proxy_pass http://backend; }

rate=10r/s表示每秒 10 个请求。burst=20表示允许短时间突发 20 个请求排入队列,但不阻塞,超出的直接拒绝。nodelay表示突发请求排队时不额外增加延迟等待时间。

第二层是访问控制。基于 IP 白名单的写法:

location /admin/ { allow 192.168.1.0/24; allow 127.0.0.1; deny all; proxy_pass http://admin_backend; }

对于管理后台这类敏感服务,IP 白名单是最直接的管控手段。记住allow和deny是顺序执行的,先匹配先生效。所以白名单写前面,deny all兜底。

6. 常见问题排查实录与面试高频点

6.1 502 Bad Gateway 问题排查

这是反向代理里出现频率最高的错误。出现 502,说明 Nginx 和后端之间的通信出问题了。排查套路按下面几步走:

第一步,确认后端进程还活着。ps -ef | grep java或者curl 127.0.0.1:8080/health,如果后端连不上自己,先解决后端问题。第二步,看防火墙。内网环境经常是后端服务监听了 127.0.0.1 而不是 0.0.0.0,Nginx 转发到这台机的公网 IP 或容器 IP 时会被拒。用ss -lntp看监听地址,确保监听的是 Nginx 能访问到的地址。第三步,看 Nginx 的错误日志,/var/log/nginx/error.log。如果看到connect() failed (111: Connection refused),说明端口没通;如果看到connect() timed out,说明网络不通或者后端负载过高。第四步,检查proxy_pass配置的上游端口和实际端口是否一致。

我见过最隐蔽的一次 502,是后端服务启动正常,日志也正常,但 Nginx 就是连不上。后来发现后端服务监听的 IPv6 地址,Nginx 用 IPv4 去访问,自然连接被拒。解决方式是让后端监听0.0.0.0:8080而不是::。

6.2 504 Gateway Timeout

504 和 502 不同,TCP 连接建立了,但后端迟迟没有返回完整响应。优先考虑proxy_read_timeout太短。自己写接口测一下后端处理耗时,如果后端要 90 秒,你设 60 秒就必然 504。其次考虑后端线程池被打满,连接虽然能建上,但一直排队得不到处理。这种情况后端日志会有线程池拒绝的报错,需要调大连接数和线程数。还有一种情况是后端处理中产生了死锁或长时间 GC,这种需要结合后端监控一起来定位。

还有一个容易忽略的点:如果 Nginx 开启了proxy_buffering off,而客户端下载断开了,Nginx 会把错误信息传给后端,导致后端写 socket 报错。排查时能看到 upstream 日志里有client prematurely closed connection,别慌,先查客户端行为再查后端。

6.3 404 与路由丢失问题

出现转发后 404,大概率是proxy_pass路径拼接不对。比如 location 是/api,proxy_pass 是http://backend/,那么请求/api/users转后变成/users,如果后端路由里预期收到的是/api/users,自然就 404。这类问题的排查方式很简单:在后端日志里看接收到的实际请求路径是什么,对比预期即可。

如果是前端刷新页面 404,而不是接口 404,那就是try_files没配置,回到单页应用那节,把try_files $uri $uri/ /index.html;加上。

6.4 高频面试题整理

结合反向代理这个主题,我在面试候选人的时候喜欢问以下几个问题,这里也一并梳理答案,给准备跳槽的朋友参考:

问题 1:Nginx 反向代理和正向代理有什么区别?

正向代理代理的是客户端,反向代理代理的是服务端。正向代理中,客户端知道目标服务器,但目标服务器不知道真实客户端是谁;反向代理中,客户端不知道真实服务器是谁,只认识 Nginx 这个网关。

问题 2:proxy_pass 末尾带/和不带/有什么区别?

不带/时,客户端请求的完整 URI 会原样传给后端;带/时,location 匹配到的前缀会被替换成/。这个之前展开过,面试时直接举例子说明即可。

问题 3:如何获取用户的真实 IP?

配置proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;,后端从请求头里取。但要提醒,如果 Nginx 前面还有负载均衡器或 CDN,单靠一层 Nginx 配置还不够,要保证链路每一层都正确追加 X-Forwarded-For,并且最外层要过滤掉伪造的 XFF 头。

问题 4:Nginx 中 location 的匹配优先级是什么?

精确匹配=,然后是^~前缀匹配,然后是正则~/~*,最后是普通前缀匹配,取最长前缀。正则按书写顺序命中即停。

问题 5:反向代理时为什么要配 proxy_set_header Host $host?

默认 Nginx 转发后端时带的是 upstream 的 IP 端口,后端如果需要基于域名做路由、生成重定向 URL、校验 Host 白名单,都会出问题。这也是很多应用转发后登录跳转异常的根因。

6.5 Nginx 平滑升级与版本漏洞处理

维护 Nginx 最需要留心的就是版本漏洞。像以往曝出的 CVE 里,就出现过利用Content-Length或Range请求头绕过访问控制的高危漏洞。作为维护者,你至少要能熟练完成平滑升级和快速回滚。

Nginx 平滑升级的原则是:启动新版本二进制,保留旧的 master 进程和 worker 进程处理存量连接,新连接交给新进程。传统手法是给旧 master 发 USR2 信号,让它拉起新 master,然后再发 WINCH 信号逐步关闭旧 worker。现在官方推荐用二进制替换 +nginx -s reload的方式,大多数发行版下也是这个思路。

在升级前,先备份当前可执行文件:

cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak

然后用新版本编译安装,安装完成后先测配置:

/usr/local/nginx/sbin/nginx -t

确认无报错后执行平滑重载:

/usr/local/nginx/sbin/nginx -s reload

reload 过程会重新加载配置,创建新的 worker 进程,旧的 worker 在完成存量连接后退出。如果升级后发现异常,把备份文件换回去再 reload 一次即可。

这里强烈建议加一个定时提醒:nginx -V出来的版本号,要去官网或者安全公告里核对是否存在已知 CVE。Nginx 的版本迭代并不频繁,但一有版本就是安全相关,别拿生产环境开玩笑。

7. 一点实践经验总结

踩过足够多的坑之后,我对 Nginx 反向代理有几点很深的体会。

配置一定要保持最小化。能在一个 location 里解决的问题,就不要拆成两个;能用默认参数跑通的场景,就不要为了“优化”去改那些看不懂的参数。Nginx 的很多默认值是非常保守且经过验证的,乱调参数反而容易引入诡异的问题。

日志是最有用的排障工具。给反向代理的 location 里加上自定义日志格式,比如记录$upstream_addr(实际后端地址)、$upstream_response_time(后端响应耗时)、$upstream_status(后端返回码),出了问题看日志一眼就能定位瓶颈。比如一条请求响应时间 3 秒,但$upstream_response_time是 5 秒,说明时间全耗在 Nginx 内部处理或客户端传输上;如果$upstream_response_time本身就是 5 秒,问题就直接定位到后端了。这个经验是真的能救命。

配置文件的变量和 include 要善用,但别滥用。多站点场景下,把每个站点的 server 配置独立成一个文件放到 conf.d 下面,按域名命名,例如conf.d/api.example.com.conf,找起来非常清爽。全局通用的 header 配置抽到一个 proxy_params 文件里,每个需要代理的 location include 一下,避免重复粘贴 20 行配置。不过 include 多了以后也要留意层级关系,别嵌套太深,否则维护成本反而上去了。

最后聊一下测试。改完 Nginx 配置,一定不要直接 reload 到生产,先用nginx -t校验语法,再在预发环境验证路径匹配和 header 透传是否符合预期。用 curl 带-H "Host: xxx"模拟不同域名的请求,用curl -v看响应头和重定向,这些都是最朴素的验证方式,但足够有效。

Nginx 反向代理这个主题,说深可以很深,说浅也真的很浅。掌握了 location 匹配规则、proxy_pass 路径拼接、header 透传、超时和缓冲参数调优这几板斧,日常绝大部分场景都不会再有难住你的地方。剩下那些机制层面的细节,比如 epoll 模型、共享内存、lua 扩展、动态 upstream 模块,等你在实际业务里遇到瓶颈再去深挖,反而记得更牢,理解也更透彻。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询