要配置Nginx的HTTP/2 Server Push,首先要理解它的工作方式。HTTP/2引入的Server Push允许服务器在响应主文档时,提前把页面依赖的CSS、JavaScript、图片等资源一并推送给客户端,从而省去浏览器解析HTML后再发起请求的往返时间。Nginx从1.13.9版本开始支持这一特性,通过http2_push_preload指令可以自动读取上游应用或本地配置中返回的Link响应头,并触发对应资源的推送。下面这张图展示了Nginx与OpenTSDB协同工作的整体数据流。

HTTP/2 Server Push在Nginx中的实现原理与基础配置
Nginx中的Server Push依赖两个核心指令:http2_push用于手动指定要推送的资源路径,http2_push_preload则让Nginx自动解析响应头里的预加载声明。当上游应用返回类似Link: </style.css>; rel=preload; as=style这样的响应头时,如果http2_push_preload被设置为on,Nginx就会在发送主响应之前先推送/style.css。这种机制的优点在于解耦了应用逻辑与推送策略,应用开发者只需要在代码里添加预加载头,不需要关心服务器层面的具体实现。
基础配置并不复杂。首先在server块中启用HTTP/2,然后打开http2_push_preload。以下是一段典型的Nginx配置:
server {
listen 443 ssl http2;
server_name ippipp.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
http2_push_preload on;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location = /style.css {
expires 7d;
add_header Cache-Control "public";
}
}
其中listen 443 ssl http2是关键,只有启用http2参数,推送功能才会生效。如果使用了http2_push_preload on,Nginx会检查上游返回的Link头,并逐个推送其中标记为rel=preload的资源。需要注意的是,推送的资源同样会走常规的缓存策略,如果浏览器已经缓存了该资源,推送可能会被忽略或导致不必要的带宽消耗。因此,生产环境需要谨慎控制推送范围。
定制Nginx访问日志格式以记录推送详情
默认的combined日志格式只包含请求方法、URI、状态码、字节数、引用页和用户代理,无法反映HTTP/2推送的具体行为。要监控推送效果,必须自定义log_format,把协议版本、推送相关响应头、请求耗时等信息记录下来。Nginx提供了$http2变量表示当前连接是否为HTTP/2,而推送的触发通常与响应中的Link头直接相关,因此可以通过$sent_http_link来记录实际返回的Link头内容。
下面是一个适合推送监控的日志格式示例:
log_format push_log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" proto=$server_protocol '
'http2=$http2 link="$sent_http_link" '
'request_time=$request_time upstream_time=$upstream_response_time';
这个格式在常规信息之外增加了proto、http2、link、request_time和upstream_time字段。其中$sent_http_link会在响应头包含Link时输出完整内容,例如</style.css>; rel=preload; as=style。有了这些数据,运维人员就可以在日志分析平台中筛选出所有HTTP/2请求,统计推送资源的类型、数量以及对应的耗时分布。不过,如果只想记录推送是否成功,还需要结合客户端行为数据,因为Nginx无法直接知道浏览器是否真正使用了推送的资源。
将Nginx日志接入OpenTSDB的两种方案
OpenTSDB是基于HBase的时序数据库,适合存储和查询带时间戳的指标数据。要把Nginx的推送日志变成可查询的指标,常见做法有两种:一是通过Filebeat或Logstash采集日志文件,解析后调用OpenTSDB的HTTP API写入;二是使用OpenResty(Nginx+Lua)在处理请求时直接异步发送数据到OpenTSDB。
第一种方案对现有架构侵入小,适合已经部署了ELK或类似日志管道的团队。Filebeat可以监控Nginx的access日志文件,通过配置processors解析出自定义格式,然后输出到OpenTSDB。由于Filebeat原生不支持OpenTSDB输出,通常需要配合Logstash的http输出插件,或者自己写一个小的转发服务。假设已经有一个接收JSON并写入OpenTSDB的中间件,Filebeat配置可以如下:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
fields:
service: nginx_push
fields_under_root: true
multiline.pattern: '^\['
multiline.negate: true
multiline.match: after
output.http:
hosts: ["http://opentsdb-middleware:8080/put"]
http_method: "post"
format: json
第二种方案更实时,利用OpenResty的ngx.timer.at在请求结束后异步发送数据,不会阻塞正常的响应流程。下面是一个Lua代码片段,它在log_by_lua_block阶段把关键指标打包发送到OpenTSDB的/api/put接口:
local cjson = require "cjson"
local http = require "resty.http"
local function push_to_opentsdb(premature, metric, value, tags)
if premature then return end
local httpc = http.new()
local body = {
metric = metric,
timestamp = os.time(),
value = value,
tags = tags
}
local res, err = httpc:request_uri("http://opentsdb-host:4242/api/put", {
method = "POST",
body = cjson.encode(body),
headers = { ["Content-Type"] = "application/json" }
})
if not res then
ngx.log(ngx.ERR, "OpenTSDB push failed: ", err)
end
end
local link = ngx.var.sent_http_link or ""
local is_http2 = ngx.var.http2 or "0"
if is_http2 == "1" and link ~= "" then
local tags = {
host = ngx.var.host,
resource = link
}
ngx.timer.at(0, push_to_opentsdb, "nginx.push.count", 1, tags)
ngx.timer.at(0, push_to_opentsdb, "nginx.push.request_time", tonumber(ngx.var.request_time) or 0, tags)
end
这段代码在log_by_lua_block中运行,判断当前是否为HTTP/2且响应包含Link头,然后通过定时器异步发送推送次数和请求耗时到OpenTSDB。异步发送保证了即使OpenTSDB暂时不可用,也不会影响用户请求。生产环境中建议增加重试和本地缓冲机制,避免网络抖动导致数据丢失。
控制推送范围与性能考量
Server Push虽然能减少往返,但推送过多资源会带来明显的副作用。如果用户浏览器已经缓存了某资源,服务器仍然推送,就会浪费带宽并占用HTTP/2流。特别是在移动网络或高并发场景下,盲目推送所有预加载项可能适得其反。因此,需要根据用户请求路径、Cookie或User-Agent动态决定是否返回Link头,从而控制推送范围。
Nginx的map指令可以很好地实现这一需求。例如,只对首次访问的用户推送CSS和JS,已经带有特定Cookie的用户则跳过推送。下面是一个通过Cookie控制推送的配置示例:
map $http_cookie $should_push {
default 1;
"~*push_seen=1" 0;
}
server {
listen 443 ssl http2;
http2_push_preload on;
location / {
if ($should_push = 0) {
add_header Link "";
}
proxy_pass http://backend;
}
}
上述配置中,如果请求头Cookie包含push_seen=1,则$should_push变量为0,随后通过add_header Link ""覆盖上游应用的Link头,阻止Nginx执行推送。同时,应用可以在用户加载完资源后设置该Cookie,确保后续导航不再触发推送。这种方法简单有效,但需要应用配合设置Cookie,并注意Cookie的过期时间和作用域。
最后,把推送次数、推送资源大小、请求耗时以及是否重复推送等指标存入OpenTSDB后,就可以利用其强大的聚合查询能力进行趋势分析。例如,按小时统计平均推送耗时,或者按资源类型统计推送命中率。当某个资源的推送频率异常升高时,OpenTSDB的告警规则可以及时通知运维人员检查是否配置错误。通过持续监控和调整,Server Push才能真正成为加速Web应用的有效手段,而不是增加服务器负担的隐形杀手。
NginxHTTP/2 Server PushOpenTSDB修改时间:2026-09-21 18:35:38