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

Nginx使用http2_push推送了diary_delete删除接口如何清理?

HTTP/2 Server Push 是 Nginx 在启用 http2 参数后提供的一种服务器主动推送能力。管理员可以在 location 块中写入 http2_push 指令,当客户端请求某个主资源时,服务器提前把关联的静态文件也一并推过去,省去浏览器解析 HTML 后再发请求的时间。问题在于,如果配置时贪图方便,把动态接口路径也塞进了推送列表,就会让浏览器在不需要的时候收到多余内容。例如 diary 模块的删除接口 /http2_push_diary_delete 如果被推送给用户,浏览器会在 push cache 中生成一条奇怪记录,不仅占用连接,还可能造成删除操作被意外触发。下面先说明这种现象的成因,再给出删除条目和防止复发的配置方案。

Nginx使用http2_push推送了diary_delete删除接口如何清理?

为什么删除接口会被 http2_push 推送给客户端

Nginx 的 http2_push 指令通常写在 location 块中,语法为 http2_push <uri>;。它只接受一个同源资源路径,表示当该 location 被命中时,服务器会额外推送这个资源。比如下面的配置就把 /http2_push_diary_delete 当成了预推送目标:

location /diary {
    http2_push /http2_push_diary_delete;
    proxy_pass http://127.0.0.1:8080;
}

这段配置看起来像是想推送日记相关的静态资源,但 /http2_push_diary_delete 明显是删除接口,不是 CSS、JS 或图片。HTTP/2 的 push promise 并不区分资源类型,只要 Nginx 认为路径存在并且配置允许,就会向客户端宣布这个资源。浏览器收到 push promise 后,会把对应请求缓存在 push cache 中,并尝试取得资源内容。如果该路径最终返回 200 且没有 no-store 限制,它还可能进入 HTTP 缓存,之后用户点击删除按钮时,浏览器可能直接从缓存里命中,导致服务端没有收到真正的删除请求。这正是需要立刻删除该推送条目的原因。

另一个常见原因是使用了通配或批量配置。例如有的管理员会在 server 块中一次性列出应用路由,把 /diary/edit、/diary/delete 等全部推给客户端,本意可能是优化移动端速度,但删除接口被推送后风险更高。检查配置最直接的方法是查看 nginx.conf 中所有 http2_push 出现的位置,找到包含 diary_delete 或 delete 字样的行,记录下所在的 server 和 location。

清理多余推送条目的三种方式

清理思路可以分为删除单行、注释整段,以及改用白名单或变量控制。单行删除适合只有一个接口被误加入的场景,直接删掉 http2_push /http2_push_diary_delete; 这一行,再执行 nginx -s reload 即可。注释整段适合临时关闭推送,比如在 location /diary 块中的其他资源不确定是否安全时,可以把整个 http2_push 配置注释掉,保留基础的反向代理逻辑。白名单方式则更可持续,只对匹配 /static/ 的资源开启推送,动态路径不会进入推送范围。

location /diary {
    http2_push /static/diary.css;
    http2_push /static/diary.js;
    proxy_pass http://127.0.0.1:8080;
}
# 或者使用 map 动态控制
map $request_uri $push_resource {
    ~^/static/.*\.css$ /static/main.css;
    ~^/static/.*\.js$ /static/main.js;
    default "";
}
location /diary {
    http2_push $push_resource;
    proxy_pass http://127.0.0.1:8080;
}

map 方式的好处是,当匹配不到 /static/ 资源时,$push_resource 为空字符串,Nginx 会忽略本次推送。这样即使 location 匹配了 /diary 下的动态路径,只要请求的 URI 不是静态资源,就不会产生无效条目。需要说明的是,map 指令要放在 http 块中,不能放在 server 或 location 块里,否则配置校验会报错。

如果删除接口已经通过前面的误推送进入了浏览器缓存,仅仅修改 Nginx 配置并不能立即清空用户浏览器里的记录。对于开发环境,可以在 Chrome DevTools 的 Application 面板中勾选 Disable cache,或者在无痕窗口重新测试。生成环境则需要依赖缓存过期策略,让资源过期后自动失效。建议对 /http2_push_diary_delete 这类动态接口在应用层设置 Cache-Control: no-store 或 Cache-Control: private, no-cache,避免删除请求被缓存。

借助 Link 响应头阻止后续误推送

Nginx 除了在配置文件中手动写 http2_push 指令外,还可以开启 http2_push_preload on,让 Nginx 解析上游应用返回的 Link 响应头,并自动把 rel=preload 的资源推送出去。这种方式的优势是推送列表由应用层动态决定,哪个接口需要预加载、哪个接口禁止推送,都可以在代码中控制。对 diary 模块来说,删除接口的响应只要不包含 Link 头,就不会触发推送。配置如下:

server {
    listen 443 ssl http2;
    server_name yourdomain.com;
    location / {
        http2_push_preload on;
        proxy_pass http://127.0.0.1:8080;
    }
}

上游应用在返回 HTML 页面时,可以添加类似 Link: </static/diary.js>; rel=preload 的响应头。Nginx 看到这个头并且 http2_push_preload 开启,就会向客户端推送对应资源。删除接口返回的 JSON 或 204 响应通常不会设置 Link,因此推送条目自然就不会出现。需要注意的是,http2_push_preload 必须和 http2 同时生效,而且要保证上游返回的路径是绝对路径且同源,否则 Nginx 不会推送。

如果项目已经统一使用 Nginx 作为静态资源服务器,可以不使用上游 Link 头,而是用 location 精确匹配静态目录。比如把推送范围限制在 location ~* \.html$ 内,只对 HTML 页面响应做推送。删除接口的路径通常不匹配这个正则,就不会被纳入推送。这样配置后,后续新加入的动态路由也不会因为复制粘贴配置而误推送。

验证配置与最终检查

完成配置修改后,不要直接 reload 了事。先用 nginx -t 检查语法,再执行 nginx -s reload 加载新的配置。如果使用 systemd 管理,可以用 systemctl reload nginx。随后使用 curl 命令查看 HTTP/2 推送情况:

nginx -t
nginx -s reload
curl --http2-prior-knowledge -I https://yourdomain.com/diary

执行 curl 时,重点观察响应头中是否还有 push 相关字段,例如 Vary 头或 Link 头。更直观的方式是打开 Chrome DevTools 的 Network 面板,在 Protocol 列查看请求是否为 h2,并在 Initiator 列检查资源是否来自 Push。如果之前有 /http2_push_diary_delete 的条目,现在不再出现,说明删除配置已经生效。

对于已经进入浏览器 push cache 或 HTTP cache 的资源,如果只是单次删除请求被错误缓存,可以在测试时主动清理浏览器缓存。开发阶段建议使用无痕模式配合禁用缓存,避免历史推送干扰判断。生产环境则应当结合版本号或缓存头策略,让以后不需要的条目自动过期。总体来看,删除多余 http2_push 条目并不复杂,核心是收紧推送范围,只允许静态可缓存资源进入推送列表,任何带有删除、修改语义的接口都应当排除。

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