大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 ,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 。
Nginx 中 http2 协议介绍与服务搭建资源推送
描述:前面《Nginx | HTTP 反向代理:gRPC 高性能远程过程调用(RPC)框架》文章中,我们介绍了 Google 推出的 gRPC 服务框架,并在 Nginx 反向代理 gPRC 服务时它也是基于 HTTP/2 实现的,HTTP2.0 协议相对于 HTP1.1 协议有很大的一个性能提升。另外,作者前面的 Nginx 演示配置中都开启了 HTTP/2 支持,但是并未对其详细介绍,和对性能到底有那些提升,以及演示其资源推送功能,所以接下来我们一一详细介绍与实践。
温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_fib851xw86fq.html
HTTP2 协议介绍
HTTP/2 是 HTTP 协议的第二个主要版本,于 2015 年发布,旨在解决 HTTP/1.x 的性能瓶颈,提升网页加载速度和网络效率,解决了队头阻塞(HTTP/1.x 中一个请求阻塞后续请求),高延迟(减少多次 TCP 连接和握手开销)以及头部冗余(重复的头部字段占用带宽),显著提升了 Web 性能,HTTP/2 已成为现代互联网的主流协议被广泛使用。
weiyigeek.top-作者个人网站启用HTTP/2.0图
HTTP/2.0 协议的核心概念:
连接(Connection):单个 TCP 连接,可以承载多个双向数据流(Stream)
流(Stream):双向通讯的数据流,每个页面可能对应一个Stream,每个流可以包含多个消息
消息(Message):每个消息包含1个或多个帧,对应 HTTP/1.x 请求和响应
帧(Frame):最小概念单元,以二进制压缩格式存储 HTTP/1.x 内容,包括 Headers 和 Data
weiyigeek.top-HTTP/2.0 协议的核心与分层图
此文,我们再详细的介绍一下 Frame 帧的格式,如下图所示:包含长度、类型、标识,消息号(4字节32位)以及数据内容等。
weiyigeek.top-HTTP/2.0 协议帧格式图
HTTP/2 主要特性与改进:
[x] 二进制传输(核心): 将文本格式(HTTP/1.x)改为二进制格式,解析更快、更高效,且减少错误。
[x] 头部压缩(核心): 使用 HPACK 算法压缩请求头,减少冗余数据(如 Cookie、User-Agent),降低带宽消耗,例如:相似请求间仅传输变化部分,大幅度减少重复头部信息的传输,算法主要包含三个部分:
-
-
- 静态字典:将常用的 header 字段整成字典,比如
-
{"method":"GET"}
-
- 就可以用单个数字 2 来表示。动态字典:没有在静态字典里面的一些头部字段,则用动态字典。Huffman 编码:压缩编码。
weiyigeek.top-HTTP/2.0 头部压缩图
- [x] 多路复用(核心): 允许在单个 TCP 连接上并行传输多个请求和响应,即传输中无序,接收时组装(利用缓存),解决了 HTTP/1.x 的队头阻塞问题,大幅提升并发效率,减少了我们的三次握手和四次握手的开关连接的一个过程,工作效率也最高。
weiyigeek.top-HTTP/2.0 多路复用图
- [x] 服务器资源推送: 服务器可以主动向客户端并行推送资源(如 HTML、CSS、JS),减少额外的请求延迟,
weiyigeek.top-HTTP/2.0 资源推送图
- [x] 自定义资源优先级:允许客户端为请求分配优先级,确保重要资源(如 HTML)优先传输,例如在某些场景中CSS 可能对重要,需要先给浏览器进行渲染。
weiyigeek.top-HTTP/2.0 资源优先级图
- [x] 更安全的默认要求:主流浏览器仅支持加密的 HTTP/2(基于 TLS/1.2+),即必须SSL全站加密,而非明文传输才能真正使用 HTTP/2。
HTTP/2.0 与 HTTP/1.x 对比如下表格所示:
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2 | 说明与影响 |
|---|---|---|---|---|
| 协议格式 | 文本协议 | 文本协议 | 二进制协议 | HTTP/2使用二进制帧传输,解析效率更高,错误率更低 |
| 连接管理 | 短连接(默认) | 持久连接(默认) | 单连接多路复用 | HTTP/2单个TCP连接可处理多个并行流,减少连接开销 |
| 并发请求 | 每个请求需新连接 | 多个TCP连接(6-8个/域名) | 单个连接内多路复用 | HTTP/2彻底解决队头阻塞,无需域名分片等优化技巧 |
| 头部传输 | 不压缩 | 不压缩 | HPACK头部压缩 | 减少重复头部传输(如Cookie、User-Agent),节省带宽40-90% |
| 数据压缩 | 可选(如gzip) | 可选(如gzip) | 可选(如gzip) | 主体压缩方式不变,但HTTP/2有更好的整体压缩 |
| 服务器推送 | 不支持 | 不支持 | 支持(主动推送) | 服务器可预测客户端需求,提前推送CSS/JS等资源 |
| 请求优先级 | 无 | 无 | 流优先级控制 | 客户端可指定资源加载优先级(如HTML优先于图片) |
| 队头阻塞 | 严重 | 存在(管道化很少用) | 基本解决(应用层) | HTTP/2仍受TCP队头阻塞影响,但应用层已优化 |
| 安全要求 | 无 | 无 | 事实上的TLS要求 | 主流浏览器只支持加密的HTTP/2(h2),非加密的h2c较少用 |
| 握手开销 | 高(每次新连接) | 中等(持久连接) | 低(复用连接) | HTTP/2减少TCP和TLS握手次数,提升加载速度 |
| 协议协商 | 无 | Upgrade机制 | ALPN扩展 | 通过TLS的ALPN在握手时协商HTTP/2,无需额外往返 |
| 错误处理 | 关闭连接 | 关闭连接 | 更精细的连接控制 | 支持重置流(RST_STREAM)而不关闭整个连接 |
| 流量控制 | 基于TCP | 基于TCP | 基于流的流量控制 | 为每个流单独控制流量,更灵活的资源管理 |
| 典型延迟 | 高 | 中等 | 低 | 多路复用和头部压缩显著减少页面加载时间 |
| 头部大小限制 | 无明确规定 | 无明确规定 | 更高效处理大头部 | 使用索引表管理头部字段,避免重复传输 |
| 缓存策略 | 基础支持 | 增强(ETag等) | 同HTTP/1.1 | 缓存机制继承HTTP/1.1,无本质改变 |
weiyigeek.top-http1.0VS2.0图
另外,HTTP/2 为 HTTP/3 奠定了基础,HTTP/3 改用 QUIC 协议(基于 UDP),进一步解决 TCP 的队头阻塞和连接延迟问题。
好了,上面我们介简单绍了 HTTP/2.0 相关特性以及与 HTTP 1.x 协议的对比,下面我们将在 Nginx 中搭建一个 HTTP/2.0 服务,查看到 HTTP 2.0 的优势,并演示其资源推送功能,不过在此之前,同样先简单的介绍在 Nginx 中 HTTP/2.0 相关指令配置。
Nginx HTTP/2 模块指令
在 Nginx 中要完全支持 HTTP/2.0 相关功能,需在编译时启用 --with-http_v2_module 选项,以安装 ngx_http_v2_module 模块,其次必须开启 TLS/SSL 协议,需申请并配置SSL证书(如通过Let's Encrypt、自签证书),ngx_http_v2_module 帮助文档地址 https://nginx.org/en/docs/http/ngx_http_v2_module.html
# 构建脚本 Makefile
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --error-log-path=/var/log/nginx/logs/error.log --http-log-path=/var/log/nginx/logs/access.log --lock-path=/var/run/nginx.lock --modules-path=/usr/local/nginx/modules --with-http_stub_status_module --with-http_realip_module --with-http_ssl_module --with-http_slice_module --with-http_v2_module --with-cc-opt=-O2 --with-compat
# 编译安装
make -j$(nproc) && make install
指令模块
http2 指令用于启用 HTTP/2.0 支持,默认情况下是关闭的。
Syntax: http2 on | off;
Default: http2 off;
Context: http, server
This directive appeared in version 1.25.1.
# 基础示例
server {
listen 443 ssl; # 开启 TLS/SSL 协议,启用 HTTPS 服务。
http2 on; # 启用 HTTP/2.0 支持。
ssl_certificate server.crt; # 指定 SSL 证书文件路径。
ssl_certificate_key server.key; # 指定 SSL 私钥文件路径。
}
资源推送指令,用于配置服务器主动向客户端推送资源。
http2_push 指令用于将请求与对原始请求的响应一起沿着发送(推送)到指定的 uri,使用相对路径也可使用变量,并且可在同一配置级别上指定多个 http2_push 指令,但自版本1.25.1起已过时,建议在头部字段中添加 Link 预加载提示。
Syntax: http2_push uri | off;
Default: http2_push off;
Context: http, server, location
# 示例
http2_push /static/css/main.css;
http2_push_preload 指令用于启用或禁用预加载推送,默认情况下是关闭的,启用后需在头部字段中添加 Link 预加载提示,以便浏览器知道哪些资源将被推送,同样自版本 1.25.1 起已过时。
Syntax: http2_push_preload on | off;
Default: http2_push_preload off;
Context: http, server, location
# 示例:若响应头含 Link: </star.css>; rel=preload,则star.css被推送。
http2_push_preload on;
请求接并发与流控制指令,用于配置 HTTP/2.0 连接并发和流控制相关参数。
http2_max_concurrent_pushes 指令用于限制连接中并发推送请求的最大数量,自版本1.25.1起已过时。
Syntax: http2_max_concurrent_pushes number;
Default: http2_max_concurrent_pushes 10;
Context: http, server
http2_max_concurrent_streams 指令用于设置连接中并发HTTP/2流的最大数量,自版本1.25.1起已过时。
Syntax: http2_max_concurrent_pushes number;
Default: http2_max_concurrent_pushes 10;
Context: http, server
http2_max_requests 指令用于设置一个HTTP/2连接可以处理的最大请求数(包括推送请求),超过此数量后,下一个客户端请求将导致连接关闭并需要建立新的连接,自版本1.25.1起已过时,建议使用 keepalive_requests 指令代替。
Syntax: http2_max_requests number;
Default: http2_max_requests 1000;
Context: http, server
http2_chunk_size 指令用于设置响应正文切片的最大块大小, 过低的值会导致更高的开销, 过高的值会由于HOL阻塞而损害优先级。
Syntax: http2_chunk_size size;
Default: http2_chunk_size 8k;
Context: http, server, location
缓冲区大小设置指令,用于配置 HTTP/2.0 连接缓冲区大小相关参数。
http2_body_preread_size 指令用于设置每个请求主体的缓冲区大小,默认情况下是 16k。
Syntax: http2_body_preread_size size;
Default: http2_body_preread_size 64k;
Context: http, server
http2_max_field_size 指令用于限制经过HPACK压缩的请求标头字段的最大大小,默认情况下是 4k。自版本1.19.7起已过时,建议使用 large_client_header_buffers 指令。
Syntax: http2_max_field_size size;
Default: http2_max_field_size 4k;
Context: http, server
http2_max_header_size 指令用于限制HPACK解压缩后整个请求标头列表的最大大小。对于大多数请求,默认限制应该足够了,自版本1.19.7起已过时,建议使用 large_client_header_buffers 指令。
Syntax: http2_max_header_size size;
Default: http2_max_header_size 16k;
Context: http, server
http2_recv_buffer_size 指令用于设置每个工作进程输入缓冲区的大小。
Syntax: http2_recv_buffer_size size;
Default: http2_recv_buffer_size 256k;
Context: http
超时控制指令,用于配置 HTTP/2.0 连接超时相关参数。
http2_idle_timeout 指令用于空闲多少时间后关闭连接,自版本1.19.7起已过时,建议使用 keepalive_timeout 指令代替。
Syntax: http2_idle_timeout time;
Default: http2_idle_timeout 3m;
Context: http, server
http2_recv_timeout 指令用于设置接收超时时间,自版本1.19.7起已过时,应该使用 client_header_timeout 指令。
Syntax: http2_recv_timeout time;
Default: http2_recv_timeout 30s;
Context: http, server
另外,该模块还提供了一个内置变量 $http2,可用于检测当前连接是否为 HTTP/2.0 连接,其变量标识符值为:
- “h2” for HTTP/2 over TLS,“h2c” for HTTP/2 over cleartext TCP, or an empty string otherwise.
实践演示
步骤 01.作者准备一些静态资源文件和自签证书【参考文章:Nginx | 核心知识150讲之SSL证书签发与HTTPS加密传输学习实践笔记】,以演示其 HTTP/2.0 资源推送功能。
$ cd /usr/local/nginx/html
$ tree .
.
├── video.html
├── img
│ └── bg.png
├── video
│ └── 202403192224.mp4
├── css
│ └── style.css
├── js
│ └── index.js
└── index.html
<!-- 其中 index.html 首页,内容如下 -->
tee index.html <<'EOF'
<HTML>
<HEAD>
<meta charset="UTF-8">
<title>测试HTTP2.0推送</title>
</HEAD>
<BODY>
<h1>测试HTTP2.0推送</h2>
<BODY>
</HTML>
EOF
<!-- 其中创建一个视频播放页面,如下所示: -->
tee video.html <<'EOF'
<video
id="myVideo"
class="responsive-video"
src="/videos/202403192224.mp4"
poster="/img/bg.jpg"
preload="metadata"
controls
playsinline
muted
loop
width="1280"
height="720"
crossorigin="anonymous"
data-setup='{}'>
<!-- 多格式源文件,确保浏览器兼容性 -->
<!-- <source src="/videos/sample.mp4" type="video/mp4">
<source src="/videos/sample.webm" type="video/webm">
<source src="/videos/sample.ogv" type="video/ogg">
-->
<!-- 字幕/字幕轨道 -->
<!-- <track kind="subtitles" src="/videos/captions.vtt" srclang="zh" label="中文">
<track kind="subtitles" src="/videos/captions-en.vtt" srclang="en" label="English">
<track kind="chapters" src="/videos/chapters.vtt" srclang="zh">
-->
<!-- 降级方案 -->
<p class="video-fallback">
您的浏览器不支持 HTML5 视频。
<a href="/videos/202403192224.mp4" download>下载视频</a>
</p>
</video>
EOF
步骤 02.配置 Nginx 启用 HTTP/2.0 支持,并开启资源推送功能,修改完毕后验证配置并重启 Nginx 服务。
tee /usr/local/nginx/conf.d/server.conf <<'EOF'
# 只在连接为 HTTP/2 且支持推送时执行推送
map $http2$push_allowed {
default "";
"h2" "1";
}
server {
# 监听 443 端口,启用 TLS/SSL 协议
listen 443 ssl;
# 虚拟主机服务器名称
server_name server.weiyigeek.top;
default_type text/html;
# 启用 HTTP/2 支持
http2 on;
# 日志文件
access_log /var/log/nginx/server.log main;
error_log /var/log/nginx/server.err.log debug;
# SSL 证书文件
ssl_certificate /usr/local/nginx/certs/server.crt;
# ssl_certificate_key /usr/local/nginx/certs/server.key;
# 加密的 SSL 证书密钥文件(根据需求选择)
ssl_certificate_key /usr/local/nginx/certs/server_encrypted.key;
ssl_password_file /usr/local/nginx/certs/ssl_password.txt;
# 支持的 SSL/TLS 协议版本
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
# 支持的 SSL/TLS 加密套件
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
# SSL 会话超时时间
ssl_session_timeout 10m;
# 优先使用服务器端支持的加密套件
ssl_prefer_server_ciphers on;
# 强制使用 HTTPS 访问
add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload" always;
# 根目录设置
root /usr/local/nginx/html;
# 缺省文件索引页设置,即访问根目录时默认打开的文件名
index index.html;
# 使用 Link 指定推送多个资源
location / {
# 推送 css 和 js 文件作为预加载资源
add_header Link "</css/style.css>; as=style; rel=preload, </js/index.js>; as=script; rel=preload" always;
# 缓存控制
expires 1h;
add_header Cache-Control "public, no-cache";
}
# 仅当连接为 HTTP/2 且支持推送时才执行推送
location = /video.html {
if ($push_allowed) {
# 推送 MP4 视频文件
add_header Link "</css/style.css>; as=style; rel=preload, </videos/202403192224.mp4>; as=video; type=video/mp4; rel=preload" always;
# 可选:推送视频封面图
add_header Link "</img/bg.png>; as=image; rel=preload" always;
# 缓存控制
expires 1h;
add_header Cache-Control "public, no-cache";
}
}
# 特别注意:使用 http2_push、http2_push_preload 指令推送资源已失效,Nginx 1.25.1 及以后已不支持这种方式。
location /old {
http2_push /css/style.css;
http2_push /res/202403192224.mp4;
}
}
EOF
# 验证重载
nginx -t && nginx -s reload
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
nginx: [warn] the "http2_push" directive is obsolete, ignored in /usr/local/nginx/conf.d/server.conf:77
nginx: [warn] the "http2_push" directive is obsolete, ignored in /usr/local/nginx/conf.d/server.conf:78
温馨提示:Nginx 1.25.1 及以后版本已废弃 http2_push、http2_push_preload 等指令,不再支持资源推送功能,在配置文件中使用这些指令将会被忽略,一定要使用 add_header Link 方式来实现资源推送。
步骤 03.为了防止各位看友旧版本的 curl 工具不支持 HTTP/2.0 协议,所以这里我们使用 nghttp 命令行工具来测试 Nginx HTTP/2.0 协议,可通过 Github 或者通过包管理器安装,官网地址:https://nghttp2.org/
# 方式1.通过 Github 下载版本进行编译安装
# Ubuntu/Debian
sudo apt-get install g++ make binutils autoconf automake
autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev
libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev
libc-ares-dev libjemalloc-dev libsystemd-dev cython python3-dev
# CentOS/RHEL
sudo yum install gcc gcc-c++ make cmake automake autoconf libtool
pkgconfig zlib-devel openssl-devel libxml2-devel libev-devel
jansson-devel c-ares-devel python3-devel python3-pip
# 下载源码
wget https://github.com/nghttp2/nghttp2/releases/download/v1.68.0/nghttp2-1.68.0.tar.xz
# 解压
xz -k -d nghttp2-1.68.0.tar.xz
tar -xvf nghttp2-1.68.0.tar
# 构建
./configure --enable-app
# 编译
make -j$(nproc)
# 检查产物
ls src/.libs/ # 有时临时文件在这里
deflatehd h2load inflatehd nghttp nghttpd nghttpx
# 主要工具包括:
# nghttp - HTTP/2 客户端
# nghttpd - HTTP/2 服务器
# h2load - 负载测试工具
# nghttpx - 代理服务器
# 安装
sudo make install
# 更新动态链接库缓存
sudo ldconfig /usr/local/lib
# 方式2.通过包管理器安装(版本可能过旧)
# redhat 系
yum install nghttp2
# debian 系
apt install nghttp2
weiyigeek.top-nghttp2测速工具图
步骤 04.使用 nghttp 命令行工具测试 Nginx HTTP/2.0 支持情况,
# 访问主页路径
/usr/local/bin/nghttp -ans https://nghttp2.org
***** Statistics *****
Request timing:
responseEnd: the time when last byte of response was received
relative to connectEnd
requestStart: the time just before first byte of request was sent
relative to connectEnd. If '*' is shown, this was
pushed by server.
process: responseEnd - requestStart
code: HTTP status code
size: number of bytes received as response body without
inflation.
URI: request URI
see http://www.w3.org/TR/resource-timing/#processing-model
sorted by 'complete'
id responseEnd requestStart process code size request path
1 +731.81ms +73us 731.74ms 200 6K /
3 +1.27s +732.00ms 537.07ms 200 6K /javascripts/modernizr-2.0.js
5 +1.34s +732.01ms 612.92ms 200 8K /javascripts/octopress.js
2 +1.34s * +246.02ms 1.10s 200 38K /stylesheets/screen.css
# 访问 / 路径
/usr/local/bin/nghttp -anv --no-verify https://server.weiyigeek.top/
# 访问 /video.html 路径
/usr/local/bin/nghttp -anv --no-verify https://server.weiyigeek.top/video.html
weiyigeek.top-使用 nghttp 命令行工具验证图
但经过作者测试,在访问验证 HTTP/2.0 自动推送时,建议通过主流的浏览器(Firefox、Google、Edge)进行访问 /video.html 路径时,可以更加直观的看到资源推送的效果。
weiyigeek.top-使用主流浏览器验证HTTP2.0推送图
温馨提示: 若 curl 版本够新支持--http2 参数也是可以进行检查 ALPN 协商,例如:
$ curl -V
curl 8.4.0
# 查看详细的 HTTP/2 信息
curl -i --http2 https://server.weiyigeek.top/video.html -k
HTTP/2 200
server: nginx/1.29.0
date: Sat, 10 Jan 2026 15:20:55 GMT
content-type: text/html
content-length: 931
last-modified: Sat, 10 Jan 2026 04:51:37 GMT
etag: "6961dad9-3a3"
expires: Sat, 10 Jan 2026 16:20:55 GMT
cache-control: max-age=3600
link: </css/style.css>; as=style; rel=preload, </videos/202403192224.mp4>; as=video; type=video/mp4; rel=preload
link: </img/bg.png>; as=image; rel=preload
cache-control: public, no-cache
accept-ranges: bytes
步骤 05.查看 Nginx 访问日志,可以看到资源推送的效果,由下图可知即使 index.html 中未引入 css / js 文件也将主动推送,另外在 video.html 文件中未引入 css 以及 png 文件也是一并推送的。
weiyigeek.top-利用Nginx 访问日志查看HTTP2.0消息推送图
至此,本文详细介绍了 HTTP/2.0 协议,和 Nginx 中 ngx_http_v2_module 模块指令参数,以及如何开启 HTTP/2.0 支持,并演示了其资源推送功能。
1093