1、缓存机制了解
Expires
、Cache-Control
、Last-Modified
、ETag
是和网页缓存相关的几个字段。在看如何设置之前,我们先了解一下这几个字段的作用。
1.1 强制缓存
强制缓存的含义是,当客户端请求后,会先访问缓存数据库看缓存是否存在。如果存在则直接返回;不存在则请求真的服务器,响应后再写入缓存数据库。
● Expires
这是 HTTP 1.0
的字段,用来指定资源到期的时间,是服务器端的具体的时间点。表示缓存到期时间,是一个绝对的时间 (当前时间+缓存时间),如
Expires: Thu, 10 Nov 2017 08:45:11 GMT
在响应消息头中设置这个字段之后,就可以告诉浏览器在未过期之前不需要再次请求。但是如果修改了本地时间,可能会造成缓存失效。
● Cache-control
在HTTP/1.1
中,增加了一个字段Cache-control
,该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求。
Expires
与Cache-control
的区别就是前者是绝对时间,而后者是相对时间。如下:
Cache-control: max-age=2592000
下面列举一些 Cache-control 字段常用的值:
• no-cache
:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的,只是是否使用缓存则需要经过协商缓存来验证决定。
• no-store
: 真正意义上的“不要缓存”。所有内容都不走缓存,即不使用强制缓存,也不使用协商缓存。
• public
:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)。
• private
:所有的内容只有客户端才可以缓存,中间节点不允许缓存,例如代理服务器。Cache-Control
的默认值。
• max-age
:即最大有效时间,表示缓存内容将在xxx秒后失效。
• must-revalidate
:如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。
Cache-control 的优先级高于 Expires,为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段我们都会设置。
1.2 协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。
● Last-Modified & If-Modified-Since
浏览器在第一次访问资源时,服务器返回资源的同时,在response header
中添加 Last-Modified
字段,告知浏览器资源最后一次被修改的时间,例如
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
浏览器将这个值和内容一起记录在浏览器缓存中。浏览器下一次请求这个资源,浏览器检测到有 Last-Modified
这个header
,于是添加If-Modified-Since
这个header
,值就是Last-Modified
中的值;服务器再次收到这个资源请求,会根据 If-Modified-Since
中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since
的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200。
缺点:
- 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
- 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
● Etag & If-None-Match
为了解决上述问题,出现了一组新的字段 Etag
和 If-None-Match
。
Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag
字段。之后的流程和 Last-Modified
一致,只是 Last-Modified
字段和它所表示的更新时间改变成了 Etag
字段和它所表示的文件 hash
,把 If-Modified-Since
变成了 If-None-Match
。服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。
Etag 的优先级高于 Last-Modified
更多关于缓存知识请查看:
2. Apache缓存配置例子
对于一般的纯静态页面,如html、gif、jpg、css、js
,默认安装的Apache服务器,不会在响应头添加这个字段。在Apache的配置文件中进行静态资源配置即可,这里我用的是Apache2.4
版本。
2.1 Apache设置Express
使用Apache
的mod_expires.so
模块来设置,这包括控制应答时的Expires
头内容和Cache-Control
头的max-age
指令
启用expires_module
模块:
a2enmod expires
重启Apache
:
server apache2 reload
修改apache2.conf
文件:
# 启用有效期控制
ExpiresActive On
# 图片
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 month"
# css/js
ExpiresByType text/css "access plus 4 weeks"
ExpiresByType text/javascript "access plus 4 weeks"
# html
ExpiresByType text/html "access plus 2 days"
或者
<ifmodule mod_expires.c>
# image
<filesmatch "\.(jpg|jpeg|gif|png|ico)$">
ExpiresActive on
ExpiresDefault "access plus 1 month "
</filesmatch>
# css/js
<filesmatch "\.(css|js)$">
ExpiresActive on
ExpiresDefault "access plus 4 weeks "
</filesmatch>
# html
ExpiresByType text/html "access plus 2 days"
</ifmodule>
注:当设置了expires后,在response header会自动输出Cache-Control 的max-age 信息
2.2 Apache设置Cache-Control
启用 headers_module
模块
a2enmod headers
重启 Apache
:
server apache2 reload
修改apache2.conf
文件:
# 配置mod_headers模块
<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|woff|js|css|swf)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>
2.3 Apache设置Last-Modified
一般纯静态页面本身都会有Last-Modified
信息,Apache
服务器会读取页面文件中的Last-Modified
信息,并添加到http
响应头部。
对于动态页面,如果在页面内部没有通过函数强制加上Last-Modified
,例如
header(”Last-Modified: ” . gmdate(”D, d M Y H:i:s”) . ” GMT”)
Apache
服务器会把当前时间作为Last-Modified
,返回给浏览器。
2.4 Apache设置Etag
在Apache
中设置Etag
的支持比较简单,可以通过FileETag
指令配置该选项, 例如在Apache
配置文件里面加入:
FileETag MTime Size // 大小(Size)和最后修改时间(MTime)进行Hash后得到文件的ETag的值
FileETag None // 可以使响应头不再包含ETag字段
3.完整例子
<VirtualHost *:80>
ServerName www.xxxx.com
DocumentRoot /xx/xxx/
# 配置mod_expires模块
# 启用有效期控制
ExpiresActive On
# image
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 month"
# css/js
ExpiresByType text/css "access plus 4 weeks"
ExpiresByType text/javascript "access plus 4 weeks"
# html
ExpiresByType text/html "access plus 2 days"
# 配置mod_headers模块
<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|woff|js|css|swf)$">
Header set Cache-Control "max-age=604800"
FileETag MTime Size
</FilesMatch>
<VirtualHost>