HTTP/2 服务器推送导致重复请求
HTTP/2 Server Push results in duplicate requests
A response for a document with the following headers enter Nginx:
link: </picture.jpg>; as=image; rel=preload
link: </_next/static/chunks/commons.4e96503c89eea0476d3e.module.js>; as=script; rel=preload
link: </_next/static/runtime/main-3c17cb16bbbc3efc4bb5.module.js>; as=script; rel=preload
link: </_next/static/runtime/webpack-0b10894b69bf5a718e01.module.js>; as=script; rel=preload
link: </_next/static/Q53NXtgLT1rgpqOOsVV6Q/pages/_app.module.js>; as=script; rel=preload
link: </_next/static/Q53NXtgLT1rgpqOOsVV6Q/pages/index.module.js>; as=script; rel=preload
在 HTTP/2 服务器推送的帮助下,请求被推送到客户端,但是 6 个请求中有 5 个下载了两次(一次是推送,一次是文档触发)。
Chrome Dev Tools 中的网络选项卡如下所示:
我已经测试了 Type
是否设置正确并且看起来没问题。可能是什么问题?
连续请求(chrome 启用缓存)也会产生类似的结果:
有什么问题吗?我很确定请求不应该重复
@编辑
我尝试在没有 Nginx 的情况下进行服务器推送(直接与 Node.js 后端对话,而不是后端为 Nginx 附加 link headers)。它可以正常工作。当我使用 Nginx 时,问题弹出。
顺便说一句。我知道不应该通过服务器推送来推送所有内容,尤其是图片,但我这样做只是为了一个明确的测试。如果你仔细观察,似乎只有脚本被复制,而图片只下载一次。
您的 HTML 是在正常的“凭据”连接上请求的。然后它会在该连接上推送 JPG 和 JS。
然后您的页面还会通过 anonymous
跨域设置加载。所以它不能使用推送资源并再次请求它们。
顺便说一句 recommendations are only to push a small amount 而不是页面需要的所有资源。如果你甚至想使用推送,那是因为它很复杂,而且总体上还没有真正证明收益值得复杂性。
问题的核心其实是Chromium。据我所知,这件事只在 Chromium 中失败。
Nginx 的问题在于 http2_push_preload
的实现。
Nginx 寻求的是 header 和 Link: </resource>; as=type; rel=preload
。它读取它并通过推送提供文件,不幸的是,当浏览器(我实际上只测试了 Chrome)接收到带有 Link
header 的文档以及推送它冲突导致显着变慢并下载解析文档时看到的资源。
# This results in HTTP/2 Server Push and the requests get duplicated due to the `Link` headers that were passed along
location / {
proxy_pass http://localhost:3000;
http2_push_preload on;
}
# This results in Resource Hints getting triggered in the browser.
location / {
proxy_pass http://localhost:3000;
}
# This results in a regular HTTP/2 (no push)
location / {
proxy_pass http://localhost:3000;
http2_push_preload on;
proxy_hide_header link;
}
# This result in a valid HTTP/2 Server Push (proper)
location / {
proxy_pass http://localhost:3000;
http2_push /commons.4e96503c89eea0476d3e.module.js;
http2_push /index.module.js;
http2_push /_app.module.js;
http2_push /webpack-0b10894b69bf5a718e01.module.js;
http2_push /main-3c17cb16bbbc3efc4bb5.module.js;
}
Nginx 似乎还不能很好地使用此功能...
要是我能删除 Link
header 并使用 http2_push_preload
...
就好了
无论如何我都可以使用 H2O
H2O 确实让我在保留 HTTP/2 服务器推送
的同时删除了 headers
// h2o.conf
[...]
proxy.reverse.url: "http://host.docker.internal:3000/"
header.unset: "Link"
适用于 H2O:
我希望 Nginx 修复 http2_push_preload
的工作方式并允许更多控制。
另一方面,我认为 Chromium 无论如何都应该解决这个问题,而不是下载 2 倍的字节数。
A response for a document with the following headers enter Nginx:
link: </picture.jpg>; as=image; rel=preload
link: </_next/static/chunks/commons.4e96503c89eea0476d3e.module.js>; as=script; rel=preload
link: </_next/static/runtime/main-3c17cb16bbbc3efc4bb5.module.js>; as=script; rel=preload
link: </_next/static/runtime/webpack-0b10894b69bf5a718e01.module.js>; as=script; rel=preload
link: </_next/static/Q53NXtgLT1rgpqOOsVV6Q/pages/_app.module.js>; as=script; rel=preload
link: </_next/static/Q53NXtgLT1rgpqOOsVV6Q/pages/index.module.js>; as=script; rel=preload
在 HTTP/2 服务器推送的帮助下,请求被推送到客户端,但是 6 个请求中有 5 个下载了两次(一次是推送,一次是文档触发)。
Chrome Dev Tools 中的网络选项卡如下所示:
Type
是否设置正确并且看起来没问题。可能是什么问题?
连续请求(chrome 启用缓存)也会产生类似的结果:
有什么问题吗?我很确定请求不应该重复
@编辑
我尝试在没有 Nginx 的情况下进行服务器推送(直接与 Node.js 后端对话,而不是后端为 Nginx 附加 link headers)。它可以正常工作。当我使用 Nginx 时,问题弹出。
顺便说一句。我知道不应该通过服务器推送来推送所有内容,尤其是图片,但我这样做只是为了一个明确的测试。如果你仔细观察,似乎只有脚本被复制,而图片只下载一次。
您的 HTML 是在正常的“凭据”连接上请求的。然后它会在该连接上推送 JPG 和 JS。
然后您的页面还会通过 anonymous
跨域设置加载。所以它不能使用推送资源并再次请求它们。
顺便说一句 recommendations are only to push a small amount 而不是页面需要的所有资源。如果你甚至想使用推送,那是因为它很复杂,而且总体上还没有真正证明收益值得复杂性。
问题的核心其实是Chromium。据我所知,这件事只在 Chromium 中失败。
Nginx 的问题在于 http2_push_preload
的实现。
Nginx 寻求的是 header 和 Link: </resource>; as=type; rel=preload
。它读取它并通过推送提供文件,不幸的是,当浏览器(我实际上只测试了 Chrome)接收到带有 Link
header 的文档以及推送它冲突导致显着变慢并下载解析文档时看到的资源。
# This results in HTTP/2 Server Push and the requests get duplicated due to the `Link` headers that were passed along
location / {
proxy_pass http://localhost:3000;
http2_push_preload on;
}
# This results in Resource Hints getting triggered in the browser.
location / {
proxy_pass http://localhost:3000;
}
# This results in a regular HTTP/2 (no push)
location / {
proxy_pass http://localhost:3000;
http2_push_preload on;
proxy_hide_header link;
}
# This result in a valid HTTP/2 Server Push (proper)
location / {
proxy_pass http://localhost:3000;
http2_push /commons.4e96503c89eea0476d3e.module.js;
http2_push /index.module.js;
http2_push /_app.module.js;
http2_push /webpack-0b10894b69bf5a718e01.module.js;
http2_push /main-3c17cb16bbbc3efc4bb5.module.js;
}
Nginx 似乎还不能很好地使用此功能...
要是我能删除 Link
header 并使用 http2_push_preload
...
无论如何我都可以使用 H2O H2O 确实让我在保留 HTTP/2 服务器推送
的同时删除了 headers// h2o.conf
[...]
proxy.reverse.url: "http://host.docker.internal:3000/"
header.unset: "Link"
适用于 H2O:
http2_push_preload
的工作方式并允许更多控制。
另一方面,我认为 Chromium 无论如何都应该解决这个问题,而不是下载 2 倍的字节数。