1. 前言
通过 HttpHeadersModule 模块可以设置HTTP头,但是不能重写已经存在的头,比如可能相对server头进行重写,可以添加其他的头,例如:Cache-Control,设置生存期。注意:只有在响应代码为 200、204、301、302 或 304 时才有效。同样需要注意的是,除了 Last-Modified 头外,该指令可以在输出的头列表中添加一个新的头,但是不能使用这条指令来重写已经存在的头。
2. HttpHeadersModule模块
2.1 指令
指令名称:add_header
语法:add_header name value
默认值:none
使用环境:http、server、location
功能:为HTTP 响应添加头
指令名称:expires
语法:expires [modified] time | epoch | max | off;
默认值:expires off
使用环境:http、server、location、if in location
功能:该指令用于控制是否在响应中添加一个生存期标志
off:阻止改变 Expires 和 Cache-Control 头
max:设置 Expires 头为 2037年,并设置 Cache-Control 头的 max-age 值设为 10 年,如果设置为一个非负数或者是一个时间值,那么会将 Cache-Control头的 max-age=#,即将 max-age 的值设置为 '#',这里的 '#' 将非负数或时间值转换成秒数
HTTP 协议的Cache-Control 指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置 Cache-Control 并不会影响另一个消息处理过程中的缓存处理过程。
请求时缓存指令 包括:no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached等。
响应消息中的指令 包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age
2.2 浏览器中关于 Cache 的 3 种属性
1. Cache-Control
设置相对过期时间,max-age指明以秒为单位的缓存时间,若对静态资源只缓存一次,可以设置max-age的值为315360000000 (1万年)。
Http协议的 Cache-Control 的常见取值及其组合释义:
no-cache:数据内容不能被缓存,每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器。
no-store:不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源).
private(默认):只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.
public:可以被任何缓存区缓存,如:浏览器、服务器、代理服务器等。
max-age:相对过期时间,即以秒为单位的缓存时间
no-cache,private:打开新窗口时候重新访问服务器,若设置max-age,则缓存期不访问服务器。
private,正数的max-age:后退时候不会访问服务器。
no-cache,正数的max-age:后退时会访问服务器
2. expires
设置以秒钟为单位的绝对过期时间,优先级比Cache-Control低,同时设置expires和Cache-Control则后者生效。也就是说要注意一点:Cache-Control的优先级高于expires
expires起到控制页面缓存的作用,在expires设定范围内,如果服务器页面发生了变化,客户端是不会获取新的修改数据,这时需要客户端进行刷新来获取才行。
3. Last-Modifed
该资源是最后修改时间,在浏览器下一次请求资源时,浏览器将先发送一个请求到服务器上,并附上 If-Unmodified-Since 头来说明浏览器所缓存资源的最后修改时间,如果服务器发现没有修改,则直接返回304(Not Modified)回应信息给浏览器(内容很少,几乎为0),如果服务器对比时间发现修改了,则照常返回所请求的资源。
需要注意:
(1)Last-Modified属性通常和Expires或Cache-Control属性配合使用,因为即使浏览器设置为缓存,当用户点击“刷新”按钮时,浏览器会忽略缓存向服务器发送请求,这Last-Modified将能够很好的减少回应开销。
(2)Etag将返回给浏览器一个资源ID,如果有了新版本则正常发送并附上新ID,否则返回304,但是在服务器集群情况下,每个服务器将返回不同的ID,因此不建议使用etag。
nginx 集群模式下禁止 etag:
以上描述的客户端浏览器缓存是指存储位置在客户端浏览器,但是对客户端浏览器缓存的实际设置工作是在服务器上资源中完成的。虽然上面介绍了有关于客户端浏览器缓存的属性,但是实际上对这些属性的设置工作都需要在服务器的资源中做设置。
个人理解的max-age意思是:客户端本地的缓存,在配置的生存时间内的,客户端可以直接使用,超出生存时间的,到服务器上取新数据。当然这些还要看客户端浏览器的设置。
而且浏览器的缓存行为和用户的操作也有关系:
4. 浏览器缓存
(1)expires
(2)cache-control
(3)last-modified
(4)etag
每个状态的详细说明:
1. expires
expires 用于设置缓存过期时间,这里设置 expires =1m 也就是缓存60秒,这60秒钟,如果用户是地址栏回车操作,链接会返回HTTP 200状态,但是不会去服务器获取数据,就算服务器页面发生了修改,也要等待 expires 时间结束,才会去服务器对比 Last-Modified 时间。如果用户的操作是直接刷新,那么每次都会去服务器对比 Last-Modified时间。因此,服务器设置的缓存,与用户的操作息息相关。
2. Last-Modified
在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记(HttpReponse Header)此文件在服务器端最后被修改的时间。客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头(HttpRequest Header),询问该时间之后文件是否有被修改,如果服务器端的资源没有变化,则自动返回HTTP304(NotChanged.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
3. etag 工作原理
HTTP协议规格说明定义ETag为“被请求变量的实体标记”。简单点即服务器响应时给请求URL标记,并在HTTP响应头中将其传送到客户端,类似服务器端返回的格式:
如果ETag没改变,则返回状态304。当下次需要发Request索要同一个URI的时候,浏览器同时发出一个If-None-Match报头(Http RequestHeader)此时包头中信息包含上次访问得到的Etag标识。这样,Client端等于Cache了两份,服务器端就会比对2者的etag。如果If-None-Match为False,不返回200,返回304(Not Modified) Response。
4. Last-Modified 和 expires
Last-Modified 标识能够节省一点带宽,但是还是逃不掉发一个HTTP请求出去,而且要和Expires一起用。而Expires标识却使得浏览器干脆连HTTP请求都不用发,比如当用户F5或者点击Refresh按钮的时候就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以,Last-Modified还是要用的,而且要和Expires一起用。
5. etag 和 expires
如果服务器端同时设置了Etag和Expires时,Etag原理同样,即与 Last-Modified/Etag 对应的 HttpRequestHeader:If-Modified-Since 和 If-None-Match。我们可以看到这两个Header的值和WebServer发出的Last-Modified,Etag值完全一样;在完全匹配If-Modified-Since和If-None-Match即检查完修改时间和Etag之后,服务器才能返回304.
6. Last-Modified和Etag
分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败. 分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样)Last-Modified和ETags请求的http报头一起使用,服务器首先产生Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改,来决定文件是否继续缓存
一个请求过程:
(1)客户端请求一个页面(A)
(2)服务器返回页面A,并在给A加上一个Last-Modified/Etag。
(3)客户端展示该页面,并将页面连同Last-Modified/Etag一起缓存。
(4)客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/Etag一起传递给服务器。
(5)服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。
需要注意:
(1)Last-Modified和Etag头都是由WebServer发出的HttpReponse Header,WebServer应该同时支持这两种header
(2)WebServer发送完Last-Modified/Etag头给客户端后,客户端会缓存这些头;
(3)客户端再次发起相同页面的请求时,将分别发送与Last-Modified/Etag对应的HttpRequestHeader:If-Modified-Since和If-None-Match。我们可以看到这两个Header的值和WebServer发出的Last-Modified,Etag值完全一样;
(4)通过上述值到服务器端检查,判断文件是否继续缓存
7. 关于 Cache-Control: max-age=秒 和 Expires
Expires = 时间,HTTP 1.0 版本,缓存的载止时间,允许客户端在这个时间之前不去检查(发请求)
max-age = 秒,HTTP 1.1版本,资源在本地缓存多少秒。
如果max-age和Expires同时存在,则被Cache-Control的max-age覆盖。
Expires 的一个缺点: 就是返回的到期时间是服务器端的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大,那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。
Expires =max-age + “每次下载时的当前的request时间”,所以一旦重新下载的页面后,expires就重新计算一次,但last-modified不会变化.
2.3 示例
配置 last-modified(默认开启)和expires
location ~.*.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~.*.(js|css)?$ { expires 12h; }
发起请求,查看 header信息:
如果处于频繁调试阶段,可将 expires 设置为 -1s 或者 Cache_Control no-cache
add_header expires -1s;
或者
add_header Cache-Control no-cache;