为什么 Nginx 会删除分块内容的 Content-Length 标头?

为什么 Nginx 会删除分块内容的 Content-Length 标头?

我使用 nginx 1.2.3 来代理一个脚本:

proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8880;
proxy_buffering off;
proxy_read_timeout 300s;
gzip off;

脚本同时发送Transfer-encoding: chunkedContent-Length: 251

HTTP/1.0 307 Temporary Redirect
Content-length: 251
Pragma: no-cache
Location: /...
Cache-control: no-cache
Transfer-encoding: chunked

我两者都需要,但是 nginx 会自动删除Content-Length

HTTP/1.1 302 Found
Server: nginx/1.2.3
Content-Type: application/json; charset=utf-8
Content-Length: 58
Connection: keep-alive
Location: /...

因此,客户端不会等待块被发送。这曾经在早期版本的 nginx 中起作用。

答案1

不幸的是,我无法对 cnst 的帖子发表评论 - 因此我将在这里回答。

nginx_http_proxy模块默认以 HTTP/1.0 与上游通信。这可以通过指令进行更改proxy_http_version 1.1

这也可能是您的脚本返回 HTTP/1.0 答案的原因,尽管307此版本中不存在分块编码和状态代码。

你不应该使用分块编码重定向因为这确实没有任何意义。

此外,好像是 nginx不传递块从上游到客户端一个接一个,但它缓冲上游的响应Content-Length由于违反了定义,因此忽略了标头字段。我不得不查看源代码因为所有这些似乎都没有记录。

您可能想要尝试将nginx_tcp_proxy_module分块内容代理为原始 TCP 数据:Github 上的模块


更新(10.04.14)
nginx_http_proxy模块支持X-Accel-* 标题,其中一个(X-Accel-Buffering: yes|no)控制响应是否应该缓冲。

将此标头(X-Accel-Buffering: no)添加到后端的响应将导致 nginx 直接将块传递给客户端。

此标头允许控制根据请求

该模块还具有配置指令 proxy_buffering启用或禁用响应缓冲(不缓冲意味着发送块将起作用)。

代理缓冲(基于标头和指令)已记录这里

答案2

正如 Lukas 所暗示的,Content-Length如果有Transfer-Encoding集合,HTTP 1.1 就会禁止。

引用http://www.ietf.org/rfc/rfc2616.txt

   3.If a Content-Length header field (section 14.13) is present, its
     decimal value in OCTETs represents both the entity-length and the
     transfer-length. The Content-Length header field MUST NOT be sent
     if these two lengths are different (i.e., if a Transfer-Encoding
     header field is present). If a message is received with both a
     Transfer-Encoding header field and a Content-Length header field,
     the latter MUST be ignored.

答案3

您还没有具体阐述为什么您的脚本首先需要分块编码,尤其是在重定向响应中。

我发现这里存在很多问题。

  • Transfer-Encoding: chunked是一个HTTP/1.1功能(并且你的脚本似乎正在用HTTP/1.0标题回复)

  • 没有307HTTP/1.0

  • 的全部目的chunked是你不知道你的Content-Length会是什么,所以,chunked用来代替提供的长度Content-Length,而是在响应主体中提供长度,与实际内容混合在一起;脚本预先生成两个标题是没有意义的

我个人并不熟悉chunked,但根据http://en.wikipedia.org/wiki/Chunked_transfer_encoding并且https://www.rfc-editor.org/rfc/rfc2616#section-3.6.1,我猜测你的脚本对分块编码的整个处理可能是完全错误的。

如果上述内容仍未涵盖,而实际上并非如此,那么为什么带有307302http 状态代码的回复应采用“奇怪”的编码也不清楚。最近,nginx 邮件列表中有一个类似的讨论,讨论410 Gone和其他错误页面始终被排除在gzip压缩之外,我认为这种观点在这里同样适用。(http://mailman.nginx.org/pipermail/nginx/2013-March/037890.html

答案4

分享这个答案,我发布到SO,以防它有帮助:https://stackoverflow.com/questions/50499637/mp4-video-safari-cloudflare-nginx-rails-no-play/59348509#59348509

由于块未提供,我在播放 mp4 时也遇到了类似的问题,并根据下面列出的 Apple 指南确认了该问题。我确认我正在下载整个文件,但在下面的修复之后,只下载了第一个块。

curl --range 0-99 http://example.com/test.mov -o /dev/null

我通过更改 nginx.conf 中的 gzip 压缩设置解决了 Safari .mp4 播放问题,删除了.mp4文件。

以下是 nginx 中的块,供参考。(注意:根据应用程序的配置方式,您可能需要将位置行更改为location ~ \.mp4$ {

location ~ ^/(assets|system|videos)/  {
   expires max;
   add_header Cache-Control public;
   add_header ETag "";
   gzip on;
   gzip_http_version 1.1;
   gzip_vary on;
   gzip_comp_level 6;
   gzip_proxied any;

   # Reference configuration
   #gzip_types text/plain text/html text/css application/json application/javascript application/x-javascript text/javascript video/mp4 application/mp4 image/jpeg image/png image/svg+xml application/x-font-ttf application/x-font-truetype application/font-woff application/font-woff2 application/vnd.ms-fontobject;

   # Kelton trying to fix cloudflare by removing the mp4 settings
   gzip_types text/plain text/html text/css application/json application/javascript application/x-javascript text/javascript image/jpeg image/png image/svg+xml application/application/x-font-ttf application/x-font-truetype application/font-woff application/font-woff2 application/vnd.ms-fontobject;
}

Apple 文档参考链接:https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html#//apple_ref/doc/uid/TP40006514-SW6

相关内容