ETag(Entity Tag 的缩写)意思是是实体标签。是HTTP1.1规范中新增的一个HTTP头信息,也即是请求HEAD中的一个属性。ETagHTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)。
如果给定URL中的资源更改,则一定要生成新的Etag值。 因此Etags类似于指纹,也可能被某些服务器用于跟踪。 比较Etags能快速确定此资源是否变化,但也可能被跟踪服务器永久存留。
ETag的语法如下:
ETag: W/"<etag_value>"
ETag: "<etag_value>"
'W/'(大小写敏感) 表示使用弱验证器。 弱验证器很容易生成,但不利于比较。 强验证器是比较的理想选择,但很难有效地生成。 相同资源的两个弱Etag值可能语义等同,但不是每个字节都相同。
"<etag_value>"实体标签唯一地表示所请求的资源。 它们是位于双引号之间的ASCII字符串(如“675af34563dc-tr34”)。 没有明确指定生成ETag值的方法。 通常,使用内容的散列,最后修改时间戳的哈希值,或简单地使用版本号。 例如,MDN使用wiki内容的十六进制数字的哈希值。
当浏览器请求服务器的某项资源(A)时, 服务器根据A算出一个哈希值(3f80f-1b6-3e1cb03b)并通过 ETag 返回给浏览器,浏览器把"3f80f-1b6-3e1cb03b" 和 A 同时缓存在本地,当下次再次向服务器请求A时,会通过类似 If-None-Match: "3f80f-1b6-3e1cb03b" 的请求头把ETag发送给服务器,服务器再次计算A的哈希值并和浏览器返回的值做比较,如果发现A发生了变化就把A返回给浏览器(200),如果发现A没有变化就给浏览器返回一个304未修改。这样通过控制浏览器端的缓存,可以节省服务器的带宽,因为服务器不需要每次都把全量数据返回给客户端。
服务器在检测缓存的组件是否和原始服务器上的组件匹配时通常有两种方式:一是比较最新修改日期(Last-Modefied Date); 二是比较实体标签。
上面介绍了ETag的原理,接下来介绍关于比较最新修改日期的方式。比较最新修改日期时浏览器会使用If-Modified-Since头将最新修改日期与原始服务器进行比较,如果服务器上组件的最新修改日期与浏览器传回的值匹配,那么客户端将继续使用本地缓存,不解析服务器返回的值,并且HTTP的状态码返回为304,这样可以节约组件下载的时间。如果最新修改时间不同,那么客户端重新解析服务器,并获取服务器上的组件,此时返回的HTTP状态码为200。见图所示
Last-Modified与Etag类似。不过Last-Modified表示响应资源在服务器最后修改时间而已。与Etag相比,不足为:
Last-Modified与Etag类似,之所以还要使用ETag是因为Last-Modified存在一些缺点:
1) Last-Modified用于标注的最后修改的时间,但其只能精确到秒,如果某个文件在1秒钟以内,被修改多次的话,Last-Modified将不能准确标注出文件的修改时间;
2) 如果一些文件只是被定期的将时间修改了,而文件内容并没有任何变化时,Last-Modified还是会认为这个文件更新了,这样这个文件就必须从服务器端重新获取,而不无法使用缓存。
3) 如果服务器没有准确获取文件修改时间,或者与代理服务器时间不一致时,这样Last-Modified就无法精确的判断了,但使用ETag可以准确的判断。
ETag的问题在于通常使用某些属性来构造它,有些属性对于特定的部署了网站的服务器来说是唯一的。当使用集群服务器的时候,浏览器从一台服务器上获取了原始组件,之后又向另外一台不同的服务器发起条件GET请求,ETag就会出现不匹配的状况。
所以如果Last-Modified可能解决组件修改的问题,那么就不要使用ETag,直接移除即可。如果确定要使用ETag,在配置ETag的值的时候,移除可能影响到组件集群服务器验证的属性,例如只包含组件大小和时间戳。