广告位 · 页头(后台-模块管理-新建模块后替换此区域)

Nginx 中 HTTP/2 服务器推送资源的 TTL 生存时间如何设置?

HTTP/2 服务器推送(Server Push)是 HTTP/2 协议中一项重要特性,它允许服务器在收到客户端对某一资源的请求后,主动把该资源所依赖的其他资源一并发给客户端。比如当浏览器请求一个 HTML 页面时,服务器可以在响应 HTML 的同时,把页面中用到的 CSS 和 JavaScript 文件提前推送过去。这样一来,浏览器就不需要先解析 HTML 再逐个发起请求,节省了多次网络往返的时间。Nginx 作为流行的 Web 服务器,从 1.13.9 版本开始支持 http2_push 指令,让开发者可以非常方便地配置服务器推送。不过,推送的资源并非越多越好,如果推送的资源在客户端已经缓存且仍然有效,那么重复推送就会浪费带宽。因此,为推送资源配置合理的 TTL 生存时间显得尤为重要。

Nginx 中 HTTP/2 服务器推送资源的 TTL 生存时间如何设置?

在 Nginx 中控制推送资源 TTL 并不是通过一个名叫 http2_push_diary_ttl 的指令来实现的。实际上,Nginx 没有内置这种专门针对推送记录的生存时间指令。推送资源的缓存行为主要由响应头中的 Cache-Control 和 Expires 字段决定,这些头信息既作用于浏览器正常请求的资源,也作用于服务器推送的资源。只要设置得当,浏览器就会根据这些头信息判断推送资源是否过期,从而决定是使用缓存还是重新获取。

HTTP/2 服务器推送与 Nginx 的 http2_push 指令

HTTP/2 Push 的核心思想是“预加载”。服务器在响应主资源请求时,会将附加资源以 PUSH_PROMISE 帧的形式通知客户端,紧接着发送这些资源的实际内容。客户端收到 PUSH_PROMISE 后会决定是否接受推送,如果接受就缓存这些资源。Nginx 的 http2_push 指令用于在 location 或 server 上下文中手动指定要推送的 URI。例如,当用户访问根路径时,可以推送样式表和脚本文件。一个最小配置如下:

server {
    listen 443 ssl http2;
    server_name ipipp.com;
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    location / {
        http2_push /css/style.css;
        http2_push /js/app.js;
    }
}

上面的配置中,每当有请求进入根 location 时,Nginx 就会向客户端推送 /css/style.css 和 /js/app.js 这两个资源。但这里有一个问题:如果浏览器已经缓存了这两个文件,并且缓存仍然有效,那么这些推送仍然是强制性进行的,除非浏览器显式拒绝。Nginx 无法感知浏览器当前的缓存状态,因此这种手动推送方式很容易导致不必要的重复传输。

为了避免重复推送,Nginx 提供了 http2_push_preload 指令。该指令会检查响应中是否包含 Link 头,并且 Link 头中带有 rel=preload 属性,如果符合条件则自动推送对应的资源。这意味着我们可以把推送决策交给上游应用或 Nginx 的 add_header 指令,配合缓存策略实现更精准的控制。例如:

location / {
    http2_push_preload on;
    add_header Link "</css/style.css>; rel=preload; as=style";
    add_header Link "</js/app.js>; rel=preload; as=script";
}

这段配置中,add_header 添加了 Link 响应头,告知浏览器可以预加载这些资源。同时 http2_push_preload on 让 Nginx 解析这些 Link 头并主动推送。相比直接使用 http2_push,这种方式可以更灵活地与缓存控制结合,比如通过条件判断是否添加 Link 头,从而避免在缓存有效时仍然推送。

控制推送资源 TTL 的两种核心方式

推送资源的 TTL 生存时间本质上由 HTTP 缓存机制决定。浏览器在接收到推送的资源后,会根据响应头中的 Cache-Control 或 Expires 来计算该资源的缓存有效期。如果资源未过期,浏览器会优先使用本地缓存,即使服务器再次推送也不会重复存储。因此,要让推送的资源在一段时间内保持有效,就需要在 Nginx 中为这些资源设置合适的缓存头。

第一种方式是使用 Cache-Control 头。Cache-Control 是 HTTP/1.1 引入的缓存控制字段,常用值包括 max-age、public、private、no-cache 等。max-age 以秒为单位指定资源的最大有效时间。例如设置 max-age=3600 表示资源在一个小时内有效,浏览器在这段时间内不会重新请求该资源。在 Nginx 中可以通过 add_header 指令为特定 location 添加 Cache-Control 头:

location ~* \.(css|js|png|jpg|woff2)$ {
    add_header Cache-Control "public, max-age=86400";
    expires 1d;
}

第二种方式是使用 expires 指令。expires 是 Nginx 内置的指令,它会在响应中添加 Expires 头(HTTP/1.0 风格),同时也会自动生成合适的 Cache-Control 头。expires 后面可以跟具体时间,比如 expires 1h 表示一小时,expires 7d 表示七天,expires modified +30d 表示从文件修改时间起三十天。这个指令对于静态资源非常实用,因为静态文件的更新频率通常较低,可以设置较长的缓存时间。需要注意的是,如果同时使用 expires 和手动添加的 Cache-Control,Nginx 会优先采用手动添加的 Cache-Control 头,所以建议不要重复设置,避免冲突。

在实际部署中,经常将 Cache-Control 和 expires 结合使用,但一般情况下选择其一即可。对于需要频繁更新的资源,比如开发阶段的 CSS 文件,可以将 max-age 设置为一个较小的值,比如 60 秒或 300 秒,这样既利用了缓存,又能及时获取更新。对于不常变化的资源,如图片和字体文件,可以设置较长的 TTL,比如 30 天甚至一年。合理的 TTL 设置不仅能减少服务器推送的浪费,还能显著降低服务器负载和带宽消耗。

生产环境实战:完整配置与验证

下面给出一个完整的 Nginx 配置示例,演示如何在启用 HTTP/2 Push 的同时为推送资源配置合理的 TTL。该示例假设站点使用 HTTPS,证书已生成,并且静态资源存放在 /var/www/ipipp.com/static 目录下。

server {
    listen 443 ssl http2;
    server_name ipipp.com;
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    root /var/www/ipipp.com;
    location / {
        try_files $uri $uri/ =404;
        http2_push_preload on;
        add_header Link "</static/css/style.css>; rel=preload; as=style";
        add_header Link "</static/js/app.js>; rel=preload; as=script";
    }
    location /static/ {
        expires 7d;
        add_header Cache-Control "public, max-age=604800";
    }
}

配置完成后,需要重新加载 Nginx 使配置生效。可以使用 nginx -t 检查配置语法是否正确,然后执行 systemctl reload nginx 或 nginx -s reload。验证推送是否成功,可以通过浏览器的开发者工具来观察。在 Chrome 中打开 Network 面板,刷新页面后查看资源的 Initiator 列,如果显示 Push 字样,说明该资源是通过 HTTP/2 Push 推送的。另外也可以使用 curl 命令行工具查看响应头:

curl -I -k https://ipipp.com/
curl -I -k -H 'Accept: text/html' https://ipipp.com/static/css/style.css

在 curl 的响应中,可以看到 Cache-Control 头和 Expires 头,这些信息表明了资源的 TTL 设置。如果浏览器已经缓存了资源,后续访问即使服务器再次推送,浏览器也会因为缓存命中而直接使用本地副本,从而避免了重复下载。但要注意,即使浏览器拒绝推送,服务器仍然会发送 PUSH_PROMISE 和部分数据,因此推送策略的优化空间还是存在的。

常见问题与优化建议

在实际使用 HTTP/2 Push 时,一个常见的误区是认为所有静态资源都应该推送。实际上,过度的推送会消耗服务器的并发连接和带宽资源,甚至可能导致页面加载变慢。更合理的做法是只推送首屏渲染必需的少量关键资源,例如主 CSS 文件和核心 JavaScript 文件。对于图片、字体等大文件,可以通过 preload 提示让浏览器按需加载,而不是强制推送。

另一个容易忽略的问题是 Cookie 的影响。如果推送的资源依赖于 Cookie,而浏览器在首次访问时没有携带 Cookie,那么推送的资源可能不会被正确缓存。因此,建议将静态资源放在独立的无 Cookie 域名下,或者通过配置确保推送的资源不依赖 Cookie。此外,HTTP/2 Push 在某些反向代理或 CDN 场景下可能不被支持,需要确认整个链路是否都支持 HTTP/2 以及服务器推送。对于不支持 Push 的客户端或中间设备,Nginx 会自动降级,不会影响正常访问。

总之,虽然 Nginx 没有名为 http2_push_diary_ttl 的指令,但通过合理配置 Cache-Control 和 expires,完全可以控制推送资源的 TTL 生存时间。理解缓存机制与推送机制的关系,才能让 HTTP/2 Push 真正发挥出减少延迟、提升用户体验的作用。在优化过程中,建议结合真实的网络环境和用户行为数据进行调整,逐步找到适合自己站点的最佳配置。

广告位 · 文章底部(后台-模块管理替换此区域)