在Nginx中没有使用Etag头,安装Nginx的创始人Igor Sysoev的观点“对于静态资源而已,看不出Etag比Last-Modified好”,因此在Nginx中就没有用Etag。但是Etag也有自己的独到之处,它可以解决Last-Modified无法解决的问题Etag。
按照业界的分析有三种情况:
下面是文件属性的状态参数
[root@master ~]# stat README File: `README' Size: 2075 Blocks: 16 IO Block: 4096 regular file Device: fd00h/64768d Inode: 15466845 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2011-07-28 21:05:00.000000000 +0800 Modify: 2011-07-28 21:05:00.000000000 +0800 Change: 2011-11-11 11:27:18.000000000 +0800 |
u 对于修改非常频繁的文件,可能会在1秒内修改多次,由于Last-Modified是基于秒级检测的,说到根源是由于它检测的依据是被访问文件修改时间的Linux系统的时间戳,因此在秒内修改的情况下是Last-Modified无法检测出的。对于这种情况,我认为就没有没有启用缓存了。
u 对于周期性改变的文件,这种情况很常见,通常文件内容并没有发生改变而只是文件的Modify的改变,如果想测试这种情况,可也使用touch命令来touch一个已经存在的文件来测试。文件的内容并未改变,但是依据Last-Modified的原理需要重新获取。
u 有些无法精确到文件的最后修改时间,这种情况不是很多。
这两个Http头都是用于有效控制客户端缓存的,在Nginx中可也通过以下配置指令来实现:
location / { root html; index index.html index.htm; expires 10d; FileETag on; } |
第一次访问该目录下的网页时响应头如下:
再次访问的请求头和响应头如下:
再次访问的请求头和响应头如下:
分析这三次访问,以Expires为缓存的方式是使用了时间缓存,即按照RFC2616对HTTP协议的规定,在客户端第二次向服务器发出请求时,对于第一次访问请求的资源如果响应状态为200的资源,那么在这次请求中将会添加一个新的请求头:If-Modified-Since,故名思议,就是询问服务器从这个时间起,或者说是以这个时间为分割点,在这时间点之前有没有修改过这个文档,如果没有修改,那么发挥的http状态代码是304.并且同时再次发回响应头Last-Modified,注意这两个头的时间完全相同。
这就是Expires的原理。
而Etag的工作原理则与Expires完全不同,按照HTTP协议的规定,对于Etag的值么有过的的规定,而只是规定了要将Etag的值放置在一对引号(””)之内。因此在开发上就灵活了很多。在后面会讲到Nginx的第三方模块nginx-static-etags模块来实现对静态文件提供Etag。
它的原理就是通过检查这个一对引号(””)之内的值,或者是说有引号之内定义的值(就是说由变量生成的值,例如URI、文件大小等等,这由具体的开发来决定,如果你对开发感兴趣,那么可以查看nginx-static-etags的源代码,另外,如果需要自己设置If-None-Match匹配的值,就是说在nginx-static-etags模块中由etag_format指令产生的格式,那么也可以使用Nginx提供的变量来实现)。
Etag的检测是使用了If-None-Match头,在客户端第一次访问一个页面时,在服务器的响应头中会发送回一个叫做Etag的响应头,在前面的截图中我们也看到过,在第二次对同一个页面发出请求是在请求头中会有一个If-None-Match头,如果与服务器端再次生成的Etag匹配,那么表示请求的页面并没有改变,而是以304代码响应,同时再响应头中再次发回If-None-Match头。
这里就需要说一点,使用了Etag是有代价的(无论多少总是有的),它有个产生Etag和比较Etag的过程,因此会占用CPU资源。
最后一个点需要说的是,在我们同时使用了Expires和Etag之后,没有谁优先的问题,而是满足两者才会做出决定。