nginx有些版本同时开启gzip和etag会出现一些问题.md
一、Nginx 1.3.3 以后的默认打开etag
nginx从1.3.3开始新加了一个etag参数,默认是打开的。
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Fri, 15 Feb 2019 03:24:45 GMT
Content-Type: text/html
Content-Length: 4
Last-Modified: Fri, 15 Feb 2019 03:23:16 GMT
Connection: keep-alive
ETag: "5c6630a4-4"
Accept-Ranges: bytes
二、Apache中etag的生成算法
在之前的文章中我们探讨过Apache中Etag的实现,Apache默认会根据inode、文件修改时间、文件大小来生成Etag值。如下,
FileETag INode MTime Size
所以在集群服务器中,应当关闭inode选项,以免造成用户拿到etag不一致的情况。
三、Nginx中etag的生成算法
经过查看Nginx代码src/http/ngx_http_core_module.c
etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
r->headers_out.last_modified_time,
r->headers_out.content_length_n)
- etag->value.data;
r->headers_out.etag = etag;
Nginx中的etag是根据文件最后修改时间+文件大小生成的,所以不需要担心集群中inode问题造成的影响。
四、Nginx中gzip对etag的影响(从nginx1.3.3到nginx1.7.3)
但在某些版本(从nginx1.3.3到nginx1.7.3)中,当开启gzip时,会导致etag头丢失。官方给出的解释是压缩以后文件大小无法保证。
查看源代码./src/http/modules/ngx_http_gzip_filter_module.c,发现gzip模块使用了ngx_http_clear_etag(r)函数移除了etag头部信息。
ngx_str_set(&h->key, "Content-Encoding");
ngx_str_set(&h->value, "gzip");
r->headers_out.content_encoding = h;
r->main_filter_need_in_memory = 1;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
ngx_http_clear_etag(r);
五、Nginx1.7.3以后的情况
自Nginx1.7.3以后,nginx在处理gzip中遇到etag头部,会将强etag自动转换为弱etag(weak ETAG),如果遇到弱etag,则不作处理原样返回。
举个例子,Nginx对于本地文件,会使用强etag,如果开启gzip就会使用弱etag。
[root@m.ipcpu.com ~]# curl -s http://10.140.100.22/t.html -o /dev/null -vvv
<output omitted>
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Tue, 19 Nov 2019 08:18:18 GMT
< Content-Type: text/html
< Content-Length: 779
< Last-Modified: Thu, 24 Oct 2019 08:02:39 GMT
< Connection: keep-alive
< Vary: Accept-Encoding
< ETag: "5db15a9f-30b"
< Accept-Ranges: bytes
<
{ [data not shown]
##@@启用gzip压缩
[root@m.ipcpu.com ~]# curl -s http://10.140.100.22/t.html -o /dev/null -vvv --compressed
<output omitted>
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Tue, 19 Nov 2019 08:19:31 GMT
< Content-Type: text/html
< Last-Modified: Thu, 24 Oct 2019 08:02:39 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
< ETag: W/"5db15a9f-30b"
< Content-Encoding: gzip
<
{ [data not shown]
六、常用浏览器端缓存总结
Expires(HTTP/1.0)/Cache-Control(HTTP/1.1) Header是控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只是Cache-Control比Expires可以控制的多一些, 而且Cache-Control会重写Expires的规则。
Last-Modified/If-Modified-Since和ETag/If-None-Match是浏览器发送请求到服务器后判断文件是否 已经修改过,如果没有修改过就只发送一个304回给浏览器,告诉浏览器直接从自己本地的缓存取数据;如果修改过那就整个数据重新发给浏览器。
参考资料
https://github.com/billfeller/billfeller.github.io/issues/91
https://pureage.info/2015/06/25/nginx-proxy-cache-and-etag.html
https://www.zhetenga.com/view/nginx%E5%BC%80%E5%90%AFgzip%E6%A8%A1%E5%9D%97%E5%90%8EETAG%E4%B8%A2%E5%A4%B1-b666d2155.html