TL;DR
为什么这么多网站都使用“查询字符串”方法,而不是让 last-modified 标头发挥作用?
更改查询字符串会更改 url,确保内容是“新鲜的”。
我是否应该取消设置 Last-modified 标头而只使用查询字符串?
没有。虽然这几乎是正确的答案。
网络上使用了三种基本的缓存策略:
- 没有缓存,或者缓存被禁用
- 使用验证/条件请求
- 永久缓存
为了说明这三个方面,请考虑以下场景:
用户第一次访问一个网站,加载十个页面然后离开。每个页面加载相同的 css 文件。对于上述每种缓存策略,会发出多少请求?
无缓存:10 个请求
在这种情况下,应该清楚没有任何其他因素影响结果,对 css 文件的 10 次请求将导致它被发送到客户端(浏览器)10 次。
优势
缺点
验证请求:10 个请求
如果使用Last-Modified 或Etag,则也有 10 个请求。但是其中 9 个只是标题,没有正文被传输。客户端使用条件请求来避免重新下载它已经拥有的东西。以本网站的 css 文件为例。
第一次请求文件时,会发生以下情况:
$ curl -i http://cdn.sstatic.net/stackoverflow/all.css
HTTP/1.1 200 OK
Server: cloudflare-nginx
Date: Mon, 12 May 2014 07:38:31 GMT
Content-Type: text/css
Connection: keep-alive
Set-Cookie: __cfduid=d3fa9eddf76d614f83603a42f3e552f961399880311549; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.sstatic.net; HttpOnly
Cache-Control: public, max-age=604800
Last-Modified: Wed, 30 Apr 2014 22:09:37 GMT
ETag: "8026e7dfc064cf1:0"
Vary: Accept-Encoding
CF-Cache-Status: HIT
Expires: Mon, 19 May 2014 07:38:31 GMT
CF-RAY: 1294f50b2d6b08de-CDG
.avatar-change:hover{backgro.....Some KB of content
对同一网址的后续请求如下所示:
$ curl -i -H "If-Modified-Since:Wed, 30 Apr 2014 22:09:37 GMT" http://cdn.sstatic.net/stackoverflow/all.css
HTTP/1.1 304 Not Modified
Server: cloudflare-nginx
Date: Mon, 12 May 2014 07:40:11 GMT
Content-Type: text/css
Connection: keep-alive
Set-Cookie: __cfduid=d0cc5afd385060dd8ba26265f0ebf40f81399880411024; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.sstatic.net; HttpOnly
Cache-Control: public, max-age=604800
Last-Modified: Wed, 30 Apr 2014 22:09:37 GMT
ETag: "8026e7dfc064cf1:0"
Vary: Accept-Encoding
CF-Cache-Status: HIT
Expires: Mon, 19 May 2014 07:40:11 GMT
CF-RAY: 1294f778e75d04a3-CDG
注意没有正文,响应是304 Not Modified。这告诉客户端它已经拥有的(在本地缓存中)该 url 的内容仍然是新鲜的。
这并不是说这是最佳方案。使用诸如the network tab of chrome developer tools 之类的工具可以让您准确了解请求需要多长时间以及执行的操作:
由于响应没有正文,响应时间将大大缩短,因为要传输的数据更少。但仍有回应。 还有连接到远程服务器的所有开销。
优势
- 内容始终新鲜
- 仅发送了一个“完整”请求
- 仅包含标头的九个请求要小得多
- 更高效
缺点
- 仍然发出最大请求数
- 仍会引发 DNS 查找
- 仍然需要与远程服务器建立连接
- 不能离线工作
- 可能需要服务器配置
永久缓存:1 个请求
如果没有 etags,没有最后修改的标头,并且只有一个过期标头设置在很远的将来 - 只有对 url 的第一次访问才会导致与远程服务器的任何通信。这是well-known? best practice for better frontend performance。如果是这种情况,对于后续请求,客户端将从自己的缓存中读取内容,根本不与远程服务器通信。
这具有明显的性能优势,这在延迟可能很明显(委婉地说)的移动设备上尤其显着。
优势
缺点
- 网址必须更改以防止现有访问者加载过时的缓存版本
- 设置/管理的最大努力
不要使用查询字符串来破坏缓存
站点使用查询参数是为了规避客户端的缓存。当内容更改时(或者如果发布了新版本的站点),查询参数会被修改,因此当 url 发生更改时,将请求该文件的 new 版本。这比每次更改文件时重命名文件的工作量更少/更方便,但它并非没有问题,
Using query strings prevents proxy caching,在下面的引用中,作者证明来自浏览器代理缓存服务器网站的请求没有使用代理缓存:
加载 mylogo.gif?v=1.2 两次(清除其间的缓存)结果
在这些标题中:
>> GET http://stevesouders.com/mylogo.gif?v=1.2 HTTP/1.1
<< HTTP/1.0 200 OK
<< Date: Sat, 23 Aug 2008 00:19:34 GMT
<< Expires: Tue, 21 Aug 2018 00:19:34 GMT
<< X-Cache: MISS from someserver.com
<< X-Cache-Lookup: MISS from someserver.com
>> GET http://stevesouders.com/mylogo.gif?v=1.2 HTTP/1.1
<< HTTP/1.0 200 OK
<< Date: Sat, 23 Aug 2008 00:19:47 GMT
<< Expires: Tue, 21 Aug 2018 00:19:47 GMT
<< X-Cache: MISS from someserver.com
<< X-Cache-Lookup: MISS from someserver.com
很明显,代理没有提供第二个响应:
缓存响应标头说 MISS,Date 和 Expires 值发生变化,
并跟踪 stevesouders.com 访问日志显示两个命中。
不应掉以轻心 - 当访问物理上位于世界另一端的网站时,响应时间可能会非常慢。从位于路由沿线的代理服务器获得答案可能意味着网站是否可用之间的区别 - 在永久缓存资源的情况下,这意味着 url 的第一次加载很慢,在使用验证请求的情况下它意味着整个网站会很慢。
取而代之的是版本控制资产
“最佳”解决方案是对文件进行版本控制,这样每当内容更改时,url 也会随之更改。通常,这将作为构建过程的一部分自动执行。
然而,一个近乎妥协的是实现重写规则such as
# ------------------------------------------------------------------------------
# | Filename-based cache busting |
# ------------------------------------------------------------------------------
# If you're not using a build process to manage your filename version revving,
# you might want to consider enabling the following directives to route all
# requests such as `/css/style.12345.css` to `/css/style.css`.
# To understand why this is important and a better idea than `*.css?v231`, read:
# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpe?g|gif)$ $1.$3 [L]
</IfModule>
这样,foo.123.css 的请求被服务器处理为foo.css - 这具有使用查询参数进行缓存清除的所有优点,但没有禁用代理的问题缓存。