
[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ]
📢 大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 🚀,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 😋 。
0x00 前言简述
描述:上一章《Nginx | 核心知识150讲,百万并发下性能优化之反向代理流程与负载均衡模块介绍笔记》作者介绍了 Nginx 负载均衡以及反向代理的基础知识,通过参考 Nginx 官方文档,并结合实际案例,详细讲解实践了 Nginx 中常用的负载均衡算法,例如轮询、加权轮询、IP 哈希(一致性)、以及最小连接等。
本章将在此基础上,继续讲解 Nginx 反向代理模块相关指令的配置,以及反向代理不同阶段的缓存配置,并针对不同的应用场景做优化配置,提高访问性能,最后通过一些小小案例,让各位看友可快速掌握如何在Nginx配置反向代理,不过在此之前,我们先来复习一下 Nginx 中 HTTP 协议反向代理请求处理流程,以便于对 Nginx 反向代理有更深层次的理解,在后续实践时能与此相互印证。
温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_fkims9wlo8ww.html
Nginx 中 HTTP 反向代理流程
下图展示了 Nginx 中 proxy 模块反向代理的处理流程,包括请求接收、缓存检查、上下游请求与响应的缓冲机制、负载均衡选择及连接复用等核心环节。通过这些步骤,Nginx 能够有效地将客户端请求转发给后端服务器,并将服务器的响应返回给客户端,从而实现反向代理的功能,这一流程对于理解 Nginx 的工作原理和优化配置至关重要。
-
流程01.反向代理在 Nginx 十一阶段之一的 content 阶段生效,此阶段负责生成发往用户的响应内容,当配置使用了 proxy_pass 指令(
优先级比 root 指令高)时,反向代理流程在此阶段被触发。 -
流程02.反向代理流程中,首先会检查是否有缓存可用,如果有则直接返回响应, 无需向上游发起请求。如果没有缓存或缓存过期,则会进入下一步处理。
-
流程03.未命中缓存时,将进入完整的反向代理流程,首先是首先生成发往上流的HTTP头部和包体(body),而非立即建立TCP连接,因为提前生成请求可避免对上游应用服务器造成不必要的连接压力,延迟建连可优化资源使用。
-
流程04.判断
proxy_request_buffering指令控制是否先将用户请求的完整包体缓存后再处理,缺省为 on 表示启用缓冲 Nginx 会先读取完整的请求包体并存储至内存或磁盘。 -
开启场景(on):上游服务并发处理能力弱于 Nginx c 时,若边接收边转发,会导致与上游服务器的连接长时间保持,消耗其有限的并发连接资源。 -
关闭场景(off):上游服务不敏感于并发连接数,可关闭此功能,然后采用流式转发,提升响应实时性,但可能增加内存压力。 -
流程05.反向代理流程中,Nginx 会根据负载均衡算法(
包括哈希(hash)、轮询(round robin)、最少连接(least_conn)等)选择 upstream 块中 server 指令定义的上游服务器,若加入lua模块,还可通过 balance_by_lua 实现 gray 的 LB 策略。 -
流程06.根据所选服务器参数建立TCP连接,并可通过多种指令配置连接行为,如超时、重试等。
-
流程07.发送请求到上游服务器,若启用了
proxy_request_buffering,则在接收完全部请求体后一次性发送,否则采用边读边发的方式进行流式传输,两种发送方式会对服务器内存产生影响,缓冲模式内存可控,流式模式因上下游速率差异导致更高内存占用。 -
流程08.接收上游响应头部,在接收到完整的响应头部后,进入到下一步处理。
-
流程09.处理响应头部,接收完成后根据配置指令处理响应头部信息,与此同时
$upstream_header_time变量记录接收响应头部所耗时间。 -
流程10.根据
proxy_buffering指令控制下游响应方式,默认开启(on)表示接收完整的上游响应包体,暂存于临时文件(目的:节约内存)或内存,再统一向客户端发送响应头部和包体。 -
开启场景(on):快速接收上游响应,释放上游连接,独立控制向客户端的发送,提高系统吞吐。 -
关闭场景(off):采用流式传输,边接收上游响应边转发给客户端, 受限于下游网速,若都客户端都在内网则影响不大。 -
流程11.缓存写入判断,响应发送完毕后,判断是否开启缓存(cache),若开启且当前响应可被缓存,则将其加入缓存系统。
-
流程12.反向代理流程结束,根据配置决定是否关闭或复用与上游的连接,复用连接通过 keepalive 机制实现,提升性能,至此整个请求处理完毕。
由上述可知 proxy 反向代理完整处理流程,涵盖缓存检查 → 请求构造 → 包体接收控制 → 负载均衡选服 → 连接建立 → 请求发送 → 响应头接收 → 响应体缓冲控制 → 响应转发 → 缓存写入 → 连接管理,后续章节将围绕此流程详解各指令的具体用法。
0x01 Nginx 反向代理模块指令浅析
描述:说到反向代理,通过 Nginx 官方文档可知,在 http 和 stream 都有 proxy 模块,即 ngx_http_proxy_module 模块和 ngx_stream_proxy_module 模块,分别用于处理转发 HTTP、gPRC、Websocket,和 TCP、UDP 协议到上游服务器上,接下来我们先来了解下这两个模块的常用指令。
ngx_http_proxy_module 模块指令
HTTP 协议反向代理
描述:在 Nginx 中 Content 阶段通过指令 proxy_pass,决定请求由 ngx_http_proxy_module 模块处理,接下来我们以反向代理最常用的 proxy_pass 指令开始介绍,逐步介绍在 http_proxy 模块中其它常用指令。
官方文档地址:https://nginx.org/en/docs/http/ngx_http_proxy_module.html
指令参数:
proxy_pass 是动作类指令用于对上游服务使用 HTTP/HTTPS 协议的反向代理,在 ngx_http_proxy_module 模块中,该指令是核心配置之一,所以其默认编译进 Nginx 中,可通过 --without-http_proxy_module 编译选项禁用。
# 设置代理服务器的协议和地址以及位置应映射到的可选URI。
Syntax: proxy_pass URL;
Default: —
Context: location, if in location, limit_except
生成发往上游的请求行:
-
proxy_method指令用于设置代理请求的 HTTP 方法,默认情况下,Nginx 会将客户端发起的原始方法传递给上游服务器。
Syntax: proxy_method method;
Default: —
Context: http, server, location
-
proxy_http_version指令用于设置代理请求的 HTTP 版本,默认情况下,HTTP 版本 1.0 传递给上游服务器,特别注意在长连接(keepalive)的场景,需要与服务端协商HTTP/1.1版本。
Syntax: proxy_http_version 1.0 | 1.1;
Default: proxy_http_version 1.0;
Context: http, server, location
生成发往上游的请求头部(Header):
-
proxy_set_header指令用于设置代理请求的头部,值可以包含文本、变量及其组合,默认情况下会设置Host头部为$proxy_host,并关闭长连接,若头字段为空字符串,则 header 不会传递给代理服务器。
Syntax: proxy_set_header field value;
Default: proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
Context: http, server, location
-
proxy_pass_request_headers指令用于控制是否将原始请求头部传递给代理服务器,默认情况下为开启。
Syntax: proxy_pass_request_headers on | off;
Default: proxy_pass_request_headers on;
Context: http, server, location
生成发往上游的请求包体(Body):
-
proxy_set_body指令定义传递给代理服务器的请求正文。
Syntax: proxy_set_body value;
Default: —
Context: http, server, location
-
proxy_pass_request_body指令用于控制是否将原始请求正文传递到代理服务器,默认情况下为开启。
Syntax: proxy_pass_request_body on | off;
Default: proxy_pass_request_body on;
Context: http, server, location
温馨提示:上述参数 URL 格式为:域名或IP地址(可选端口),Unix socket地址,和上一章所提到的 upstream 块中指定的服务组名称,以及使用@名称(内部location转发),此外还可以使用变量,例如 $scheme、$host 、$request_uri 等。
示例演示
步骤 01.在 214 主机的Nginx 配置文件中,分布定义 8011 和 8012 服务用于后续演示反向代理转发及负载均衡。
# 编辑主配置文件,添加 include 指令,引入自定义配置文件目录。
vim /usr/local/nginx/conf/nginx.conf
....
include /usr/local/nginx/conf.d/*.conf;
....
# 创建测试服务器配置文件目录,并写入两个监听服务器的配置。
mkdir /usr/local/nginx/conf.d/
tee /usr/local/nginx/conf.d/server.conf <<'EOF'
server {
listen 8011;
server_name localhost _;
default_type text/plain;
return 200 'From 8011 server response!\nrequest: $request\nhost: $host\nuri: $uri\nrequest_uri: $request_uri\nmethod: $request_method\nhttp_name: $http_name\nrequest_body: $request_body\n';
}
server {
listen 8012;
server_name localhost _;
default_type text/plain;
return 200 'From 8012 server response!\nrequest: $request\nhost: $host\nuri: $uri\nrequest_uri: $request_uri\nmethod: $request_method\nhttp_name: $http_name\nrequest_body: $request_body\n';
}
EOF
步骤 02.在 214 主机的 Nginx 配置目录中创建 backend_server.conf 文件,定义 upstream 服务组。
tee /usr/local/nginx/conf.d/backend_server.conf <<'EOF'
# 创建上游服务器
upstream backend {
# 定义共享内存区,用于在工作进程间同步负载信息,可根据后端服务器数量调整。
zone backend_zone 64k;
# 缺省使用轮询的负载均衡算法
server 127.0.0.1:8011 weight=2; # 权重为2表示,轮询三次中有两次命中此上游服务器
server 127.0.0.1:8012;
# 设置与上游服务器的长连接,最多保持10个空闲的保活连接。
keepalive 10;
keepalive_timeout 60s; # 设置与上游服务器的长连接,空闲连接的超时时间。
}
EOF
步骤 03.在 214 主机的 Nginx 配置目录中创建 proxy_server.conf 文件,定义反向代理服务,以及上述介绍的相关指令的使用案例。
-
如果 proxy_pass指令不带URI,则原始请求的URI将被传递到服务器,请求 URI 以 / 开头
# 创建监听用于反向代理
tee /usr/local/nginx/conf.d/proxy_server.conf <<'EOF'
server {
listen 80;
server_name test.weiyigeek.top;
access_log /var/log/nginx/test.log main;
error_log test.error.log info;
location / {
proxy_pass http://backend;
# 设置请求方法
proxy_method POST;
# 启用头部传递(缺省也是开启的)
proxy_pass_request_headers on;
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 Connection "";
proxy_http_version 1.1;
# 自定义头部请求
proxy_set_header name 'weiyigeek';
# 启用包体传递(缺省也是开启的)
proxy_pass_request_body off;
# 自定义body请求
proxy_set_body 'Hello World! Proxy Server By http://test.weiyigeek.top';
}
# 测试时请将下述三个需求配置的 location 代码块加入到此处。
}
EOF
然后,在 location 块中演示下述其它三个需求配置。
-
如果 proxy_pass指令指定了URI,则当请求被传递到服务器时,规范化请求URI中与位置匹配的部分将被指令中指定的URI替换。
location /a {
proxy_pass http://backend/addurl;
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 Connection "";
proxy_set_header name 'weiyigeek';
proxy_http_version 1.1;
}
如果 proxy_pass 指令中使用了请求URI变量 $request_uri,将会根据请求变量动态构建代理服务器的地址。
location /b {
proxy_pass http://backend$request_uri;
proxy_method GET;
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 Connection "";
proxy_set_header name 'weiyigeek';
proxy_http_version 1.1;
}
如果需对于更复杂的URI判断逻辑,可用 rewrite 指令使用 regular expression 匹配元组更改 URI时,将使用相同的配置来处理请求(break),此时假若,请求 /reg/xxx 请求,则转发到后端服务器的为 /xxx。
location ^~ /reg/ {
rewrite ^/reg(/|$)(.*) /$2 break;
proxy_pass http://backend;
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 Connection "";
proxy_set_header name 'weiyigeek';
proxy_http_version 1.1;
}
温馨提示:在使用 proxy_pass 指令时,务必确保上游服务器能够正确处理传递的请求URI,很多新手朋友在从不带URI 改为带 URI 时,未意识到路径替换机制,上游服务无法正确处理请求路径,返回404或逻辑错误;所以在实践时需根据是否配置 URI 路径,采用不同的URI转发策略,需明确理解其替换行为,并在测试环境中验证配置的正确性,从而避免因路径替换导致服务不可用。
步骤 04.同样,完成配置后使用 nginx -s reload 重载配置,并使用 curl 模拟请求进行验证。
$ curl http://test.weiyigeek.top
From 8011 server response!
request: POST / HTTP/1.1
host: test.weiyigeek.top
uri: /
request_uri: /
method: POST
http_name: weiyigeek
request_body:
$ curl http://test.weiyigeek.top/test/
From 8012 server response!
request: POST /test/ HTTP/1.1
host: test.weiyigeek.top
uri: /test/
request_uri: /test/
method: POST
http_name: weiyigeek
request_body:
温馨提示:$request_body 变量通常不包含由 proxy_set_body 修改后的内容,因为它是在请求被发送到后端之前就已经确定了,可通过 tcpdump -i lo port 8011 -A -s 0 命令抓包进行查看,内容如下所示:
其它三个示例的验证如下所示,其中 8011 端口的上游服务器由于权重设置为2,所以转发到8011端口的请求为2次,转发到 8012 端口的上游服务器为 1 次。
# 示例2.使用 proxy_pass 指令指定了URI目录的示例验证
$ curl http://test.weiyigeek.top/a/bb/cc?uid=1024
From 8011 server response!
request: GET /addurl/bb/cc?uid=1024 HTTP/1.1
host: test.weiyigeek.top
uri: /addurl/bb/cc # 关键点:此处URI已经被替换为 /addurl/bb/cc
request_uri: /addurl/bb/cc?uid=1024
method: GET
http_name: weiyigeek
request_body:
# 示例3.使用 $request_uri 变量动态构建代理服务器的地址的示例验证
$ curl http://test.weiyigeek.top/b/cc/dd?id=1024
From 8011 server response!
request: GET /b/cc/dd?id=1024 HTTP/1.1
host: test.weiyigeek.top
uri: /b/cc/dd # 关键点:此处URI没有被替换,request_uri 变量值为 /b/cc/dd
request_uri: /b/cc/dd?id=1024
method: GET
http_name: weiyigeek
request_body:
# 示例4.利用正则以及重写URI的示例验证
$ curl http://test.weiyigeek.top/reg/aa/bb/cc?id=1024
From 8012 server response!
request: GET /aa/bb/cc?id=1024 HTTP/1.1 # 关键点: 请求的URI,使用重写指令将其替换为 /aa/bb/cc
host: test.weiyigeek.top
uri: /aa/bb/cc
request_uri: /aa/bb/cc?id=1024
method: GET
http_name: weiyigeek
request_body:
至此,我们已经完成了 Nginx 中最简单的反向代理与负载均衡服务器的配置与验证,下一小节,将继续学习在 Nginx HTTP 协议的反向代理中接收客户端请求处理相关指令。
HTTP 协议反向代理中接收客户端请求处理
描述:本小节将主要讲解 Nginx 处理用户请求包体的核心机制,包括 proxy_request_buffering 指令在反向代理中接收客户端包体的控制机制,client_body 相关指令配置、临时文件存储路径与层级设计、以及超时控制等关键配置对性能的影响。
指令参数
1.反向代理中接收客户端包体的控制机制
-
proxy_request_buffering指令控制是否在转发到上游服务器前先接收完整客户端请求包体,其中 on 表示接收完整个 body 再向上游发送,off 表示边接收边转发,若选择关闭,则需注意的是一旦向 upstream 发送body 数据时,proxy_next_upstream指令失效,无法进行后续的故障转移或重试。 -
开启: 适用于客户端网速慢,上游服务并非处理能力弱,以及高并发场景,依赖 Nginx 本地处理能力,避免上游服务过载的场景。
-
关闭:适用于内网客户端,上游能更快接收到数据,无需等待 Nginx 接收完整包体,避免 Nginx 服务器读写磁盘的开销的场景。
Syntax: proxy_request_buffering on | off;
Default: proxy_request_buffering on;
Context: http, server, location
2.客户端包体接收缓冲区控制:https://nginx.org/en/docs/http/ngx_http_core_module.html
-
client_body_buffer_size设置阅读客户端请求正文的缓冲区大小。如果请求体大于缓冲区,则整个请求体或仅其一部分被写入临时文件。默认情况下,缓冲区大小等于两个内存页,这是x86、其他32位平台和x86-64上的8K,在其他64位平台上通常为16 K,若客户端请求包体超大则分段通过该 buffer 接收。
Syntax: client_body_buffer_size size;
Default: client_body_buffer_size 8k|16k;
Context: http, server, location
-
client_body_in_single_buffer确定 nginx 是否应该将整个客户端请求主体保存在单个缓冲区中, 建议在使用$request_body变量时使用该指令,以节省所涉及的复制操作的数量。
Syntax: client_body_in_single_buffer on | off;
Default: client_body_in_single_buffer off;
Context: http, server, location
-
client_body_in_file_only确定 nginx 是否应该将整个客户端请求主体保存在磁盘文件中。如果设置为 on,则 nginx 将不会在内存中缓存任何部分的正文,而是在磁盘上保存整个正文。如果设置为 clean,则在读取正文后将其删除,off 表示直接存入内存 buffer 中。
Syntax: client_body_in_file_only on | clean | off;
Default: client_body_in_file_only off;
Context: http, server, location
3.客户端请求体临时文件存储配置:
-
client_body_temp_path: 用于存储保存客户端请求正文的临时文件,在指定的目录下最多可以使用三个级别的XML层次结构,目的是为了防止单一目录下文件过多导致 I/O 性能问题。
Syntax: client_body_temp_path path [level1 [level2 [level3]]];
Default: client_body_temp_path client_body_temp;
Context: http, server, location
# 例如:client_body_temp_path /spool/nginx/client_temp 1 2;
# 示例:/spool/nginx/client_temp/7/45/00000123457
4.Nginx 读取请求体超时控制:
-
client_body_timeout设置读取客户端请求正文的超时时间,缺省值 60s。如果在此时间内没有完全接收正文,则连接将关闭,并返回 408(Request Time-out)错误终止。
Syntax: client_body_timeout time;
Default: client_body_timeout 60s;
Context: http, server, location
5.客户端最大请求体长度限制:
-
client_max_body_size设置客户端请求主体的最大允许大小,缺省 1m。如果请求中的主体(Content-Length)大小大于指定的限制,则返回 413(Request Entity Too Large)错误,设置为 0 表示不检查包体的大小,对于上传文件后端服务,通常需要设置更大的值,支持单位 m、g。
Syntax: client_max_body_size size;
Default: client_max_body_size 1m;
Context: http, server, location
上面学习了 Nginx 对于请求包体处理的相关指令参数,由于 Nginx 对客户端请求 body 的处理方式直接影响着 Nginx 服务器的性能表现,特别是在高并发场景下,合理的配置可以显著提升服务器的吞吐量与稳定性。所以在实际的生产环境中通常需要根据实际业务场景进行 proxy_request_buffering、client_body_* 系列指令调整,此外配置时需综合考虑下游网络状况(客户端上传速度)、上游服务处理能力及磁盘 I/O 特性等。
加入:作者【全栈工程师修炼指南】知识星球
『 全栈工程师修炼指南』星球,主要涉及全栈工程师(Full Stack Development)实践文章,持续更新包括但不限于企业SecDevOps和网络安全等保合规、安全渗透测试、编程开发、云原生(Cloud Native)、物联网工业控制(IOT)、人工智能Ai,从业书籍笔记,人生职场认识等方面资料或文章。
Q: 加入作者【全栈工程师修炼指南】星球后有啥好处?
✅ 将获得作者最新工作学习实践文章以及网盘资源。
✅ 将获得作者珍藏多年的全栈学习笔记(需连续两年及以上老星球友,也可单次购买)
✅ 将获得作者专门答疑学习交流群,解决在工作学习中的问题。
✅ 将获得作者远程支持(在作者能力范围内且合规)。

获取:作者工作学习全栈笔记
作者整理了10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者微信或者回复【工作学习实践笔记】,当前价格¥299,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

知识推荐 往期文章
若文章对你有帮助,请将它转发给更多的看友,若有疑问的小伙伴,可在评论区留言你想法哟 💬!


