Apache静态资源缓存配置

1、缓存机制了解

ExpiresCache-ControlLast-ModifiedETag 是和网页缓存相关的几个字段。在看如何设置之前,我们先了解一下这几个字段的作用。

1.1 强制缓存

强制缓存的含义是,当客户端请求后,会先访问缓存数据库看缓存是否存在。如果存在则直接返回;不存在则请求真的服务器,响应后再写入缓存数据库。

● Expires

这是 HTTP 1.0 的字段,用来指定资源到期的时间,是服务器端的具体的时间点。表示缓存到期时间,是一个绝对的时间 (当前时间+缓存时间),如

Expires: Thu, 10 Nov 2017 08:45:11 GMT

在响应消息头中设置这个字段之后,就可以告诉浏览器在未过期之前不需要再次请求。但是如果修改了本地时间,可能会造成缓存失效。

● Cache-control

HTTP/1.1中,增加了一个字段Cache-control,该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求。

ExpiresCache-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

为了解决上述问题,出现了一组新的字段 EtagIf-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

使用Apachemod_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>