前端优化中很重要的一项是设置一个较长的过期时间,例如 yahoo 的 expires 设的是 10 年,page speed 推荐一个月以上。设 expires 的目的就是让没有更新的资源不应该产生 http 请求,如果强制产生请求则返回 304,减少服务器压力和降低带宽。exprires 属于服务器优化范畴,需要修改服务器配置并重启。

http header 相关知识

expires:指定缓存到期 GMT 的绝对时间,如果设了 max-age,max-age 就会覆盖 expires。如果 expires 到期需要重新请求。

last-modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间。

ETag:就是一个对象(比如 URL)的标志值,就一个对象而言,比如一个 html 文件,如果被修改了,其 Etag 也会别修改, 所以,ETag 的作用跟 Last-Modified 的作用差不多,主要供 WEB 服务器 判断一个对象是否改变了。如果有多个服务器需要考虑 Etag 同步问题。
比如前一次请求某个 html 文件时,获得了其 ETag,当这次又请求这个文件时, 浏览器就会把先前获得的 ETag 值发送给 WEB 服务器,然后 WEB 服务器会把这个 ETag 跟该文件的当前 ETag 进行对比,然后就知道这个文件有没有改变了。

Cache-Control:
这个是 http 1.1 中为了弥补 Expires 缺陷新加入的,现在不支持 http 1.1 的浏览器已经很少了。
max-age: 指定缓存过期的相对时间秒数,max-ag=0 或者是负值,浏览器会在对应的缓存中把 Expires 设置为 1970-01-01 08:00:00 。

s-maxage: 类似于 max-age,只用在共享缓存上,比如 proxy.

public: 通常情况下需要 http 身份验证的情况,响应是不可 cahce 的,加上 public 可以使它被 cache。

no-cache: 强制浏览器在使用 cache 拷贝之前先提交一个 http 请求到源服务器进行确认。这对身份验证来说是非常有用的, 能比较好的遵守 (可以结合 public 进行考虑)。它对维持一个资源总是最新的也很有用,与此同时还不完全丧失 cache 带来的好处),因为它在本地是有拷贝的,但是在用之前都进行了确认,这样 http 请求并未减少,但可能会减少一个响应体。

no-store: 告诉浏览器在任何情况下都不要进行 cache,不在本地保留拷贝。

must-revalidate: 强制浏览器严格遵守你设置的 cache 规则。

proxy-revalidate: 强制 proxy 严格遵守你设置的 cache 规则。

用法举例: Cache-Control: max-age=3600, must-revalidate

cache: 使用本地缓存,不发生请求。

304: 通过 If-Modified-Since If-Match 判断资源是否修改,如未修改则返回 304,发生了一次请求,但请求内容长度为 0,节省了带宽。

如果有多台负载均衡的服务器,不同服务器计算出的 Etag 可能不同,这样就会造成资源的重复加载,我看的 yahoo 首页就没设 Etag,估计有这么方面的考虑。

下面我的一些分析,不敢说完全正确,只测试了 IE6 和 FF3.5,感兴趣的朋友一块讨论,毕竟浏览器对我来说是个黑洞。主要说说 expires,主要有这四个疑问

1. 设置 expires 后,请求状态应该是什么样的?是 304 还是 cache?

2. 如果资源被浏览器 cache 了,怎么才能保证资源正确更新,尤其是页面不出错。

3. 浏览器是否有差异?

4. 过期时间设为多少比较好。

为了解决上面的疑惑,我用 apche 做了测试,apache 的配置如下:

# 加载 expires 模块   
LoadModule expires_module modules/mod_expires.so   
#取消 Etag,去掉 Etag 带来的干扰   
FileETag none   
ExpiresActive On   
ExpiresDefault "access plus 10 years"   
ExpiresByType image/gif "access plus 10 years"   
ExpiresByType image/jpeg "access plus 10 years"   
ExpiresByType image/png "access plus 10 years"   
ExpiresByType text/css "access plus 10 years"   
ExpiresByType text/html "access plus 1 seconds"   
ExpiresByType text/javascript "access plus 10 years"   
ExpiresByType application/x-unknown-content-type "access plus 10 years"   
ExpiresByType application/x-javascript "access plus 10 years"

测试还需要考虑访问页面的方式,方式不同请求状态也会有差异。打开浏览器清除缓存,然后打开页面进行测试,多次测试结果如下:













































































打开页面方式 IE6(httpwatch)FF3.5(httpfox)
第一次打开页面 200200
重启浏览器打开页面 cache,即时发生资源修改也不会重新请求cache,即时发生资源修改也不会重新请求
F5 刷新304,发生修改的资源状态为 200304,发生修改的资源状态为 200
Ctrl+F5 刷新200,强制全新请求200
后退 cache, 简单直接地从缓存加载cache, 简单直接地从缓存加载
在已访问页面地址栏回车cachecache


总结:

用 yahoo 首页验证了上面的结论,除 yahoo F5 刷新页面时 IE6 下有两个请求状态为 cache 外(目前没分析到原因),二者基本一致。

把 expires 设为 30 天、14 天结果页一样。有了上面的测试结果做 expires 优化心理就有底了,疑问 1 和 3 已解决。

第二种方式打开页面,有资源修改也走本地缓存,极有可能出错,问题 2 最稳妥的办法是修改的资源启用新名称,yahoo 的作法是文件名后加版本号,这回增加前端的工作量。目前每次更新脚本库,资源地址都不变,主要为避免重命名后相关应用都需要更新的问题,节省开发的时间。

expires 设 2,3 天,有点短,稳定的应用的 js css img 更新不是很频繁,像 yahoo 设 10 年可以理解为永不过期,十年中网站不知道改版多少次了。最低设 30 天还比较合适。

本文地址 https://shaoshilei.com/2014-03/the-front-page-optimization-apache-expires-caching-strategy-reserved.html