扼杀 304,Cache-Control: immutable
随着近些年社交网站的流行,越来越多的人学会了“刷”网页 ── 刷微博,刷朋友圈,刷新闻,刷秒杀页。这里的“刷”,就是刷新的意思,在浏览器里,你可以通过点击刷新按钮,或者用快捷键,或者移动端的下拉操作来进行刷新。
但普通网民不知道的是,通过刷新操作导致的页面加载和通过其他操作(比如点击页面链接,地址栏输入网址并回车,点击收藏夹网址等)导致的页面加载有一点不同,那就是刷新操作会给该页面的请求本身以及页面里所引用的资源们(JS,CSS,图片等)的请求加上 If-Modified-Since 和 If-None-Match 请求头(如果已经有缓存且有 Last-Modified/ETag 响应头的话),服务器会根据这两个请求头判断该资源有没有更新过,如果没有,就返回不带响应体的 304 响应,告诉浏览器:“用缓存吧”,如果更新过了,则把更新后的资源放在响应体里返回 200 响应。
我们把上面说的这种带有 If-Modified-Since 或 If-None-Match 请求头的 HTTP 请求叫做条件请求,除了刷新操作,条件请求还会发生在缓存过期的时候,也就是已缓存时长大于 Cache-Control 响应头中的 max-age 字段指定的秒数的时候。
条件请求是设计用来更新资源的,但实际情况是,在现如今的网站开发中,尤其是大型网站,会依赖适当的过期时长或者让用户手动刷新来更新页面吗?比如把缓存时长设置成一小时,新版页面上线了,用户都看不到效果,老板过来问:“这怎么回事啊,不是上线了吗”,开发回答:“要等一小时缓存过期啊,你也可以刷新一下就看到效果了~”。显然不可能这样,对于那些有更新需求的静态资源,常见的是 JS、CSS,我们都会在它的 URL 里加上点东西,时间戳、版本号、哈希值等,可以放在 URL 的路径里,也可以放在查询参数里,因为只要 URL 变了,浏览器就认为是不同的资源,就会重新下载;还有一些静态资源是完全没有更新需求的,比如你在微博上传的那些图片,同一个 URL 对应的资源是永远不会变的。
上面说的这两种情况,其实是一种,就是它们永远没有更新的需求,它们是不可变的,是 immutable 的,304 用在它们身上完全没有意义,全是浪费。虽然每个 304 请求的往返体积只有 1k 左右,但架不住多啊。而且就算只有一个字节,也会导致页面展现变慢,读本地文件和读网络资源还是有本质区别的。
Facebook 在一年前意识到了这个问题,它的工程师给制定 HTTP 标准的 IETF 工作组发了封邮件,里面说到,Facebook 使用版本号来更新静态资源,还给静态资源设置了几乎不可能过期的缓存时长,但发现仍然有 20% 的请求是无意义的条件请求(必然 304),这给服务器性能带来很大伤害,他们研究发现是因为 Facebook 页面 pv 有 2% 来自用户的刷新操作,他们希望 HTTP 协议能给 Cache-Control 响应头增加一个属性字段表明该资源永不过期,浏览器就没必要再为这些资源发送条件请求了。
今年四月份,Mozilla 的人觉的 Facebook 提的这个建议很好,于是他们在 Firefox 49 里实现了 Cache-Control: immutable。immutable 的推荐用法是和那些超大的 max-age 配合使用,比如 1年:Cache-Control: max-age=31536000, immutable,甚至 10年, 但通常情况下,1 年就够了,因为:1. 对于单个缓存来说,它在某个浏览器里存活的时长不可能超过一年,浏览器的缓存空间都有上限,Firefox 256M,Chrome 320M,旧的缓存会时不时被清掉。2. 一个用户不大可能一年后还来同一个页面,且那个页面还没改版。对缓存时长来说,1 年就代表永远了。
但这只是推荐做法,immutable 并不是真的只能应用在那些永不过期的资源上,也可以配合较小的 max-age 来使用,比如一些个人博客,或者一些不太讲究及时更新的站点,可以设置成 Cache-Control: max-age=3600, immutable,表明该资源能存活一小时,在一小时之内,即便用户刷新也不要发送条件请求,在过期之后,浏览器会发送不带一个不带 If-Modified-Since 和 If-None-Match 的请求来更新资源,这里需要注意,一旦被标志成 immutable,则这个资源不可能返回 304 响应了,只有 200。
目前 Firefox 的实现里,只对 HTTPS 资源开放 immutable 属性的支持,我通过 Fiddler 在本地篡改了淘宝搜索页面 https://s.taobao.com/search?q=连衣裙 里所有资源的 Cache-Control 响应头,在原值尾部加上 “, immutable”。篡改之前,假如我刷新一下此页面,会导致数十个 304 响应:

篡改之后的刷新效果:
注意那些带有 cached 字样的 200 请求,那些请求实际上根本不是真正的请求,只是一次本地读取文件的操作。
目前 Facebook 还没有反馈 immutable 的测试数据,毕竟 Firefox 49 还不是正式版,以后应该会有的。不过考虑到现在 Firefox 的市场占有率,也许 Chrome 实现之后才会得到更多人的关注, Chrome 也表示了有意愿去实现。不过我在 GitHub 搜了一下,倒是发现 W3C 的网站和 Firefox 附加组件网站准备实现。
immutable 只有在你的网站被频繁刷新的情况下才有较大的意义。还有虽然它是向后兼容的,但可能一些 CDN 服务器在识别 Cache-Control 时因不认识这个属性,导致最终返回给浏览器的响应丢失了 immutable,推特上有反应 Akamai 就这么干了。
少数人知道的强制刷新功能(Ctrl+F5/Shift+Command+R)以及开发者工具的跳过缓存功能优先级应比 immutable 更高。
扼杀 304,Cache-Control: immutable的更多相关文章
- [转]ASP.NET Core: Static Files cache control using HTTP Headers
本文转自:https://www.ryadel.com/en/asp-net-core-static-files-cache-control-using-http-headers/ Every sea ...
- 网站 cache control 最佳实践
推荐阅读: 2020年软件开发趋势 高并发案例 - 库存超发问题 负载均衡的分类及算法 异地多活架构 Postman 的替代品来了 有时,当第二次访问网站时,看起来比较怪,样式不正常. 通常,是因为 ...
- 关于缓存和 Chrome 的“新版刷新”
在读本文前你要确保读过我的上篇文章<扼杀 304,Cache-Control: immutable>,因为本文是接着上文写的.上文说到,在现代 Web 上,“条件请求/304 响应”绝大多 ...
- 淘宝网站上的 HTTP 缓存问题两则
在阅读本文前推荐你先阅读我的前两篇文章< 扼杀 304,Cache-Control: immutable>和<关于缓存和 Chrome 的“新版刷新”>:下面要说的两个问题是在 ...
- Symfony2学习笔记之HTTP Cache
富web应用程序的本质意味着它们的动态.无论你的应用程序多么有效率,每个请求比起静态文件来说总会存在很多的耗费.对于大多数web程序来说,这没什么. Symfony2非常的轻快,无论你做些严重超载的请 ...
- HTTP请求中的缓存(cache)机制
http://www.chaorenmao.com/blog/?p=79 流程 当资源第一次被访问的时候,HTTP头部如下 (Request-Line) GET /a.html HTTP/1.1Ho ...
- Partitioned Replacement for Cache Memory
In a particular embodiment, a circuit device includes a translation look-aside buffer (TLB) configur ...
- CDN之Web Cache
1. Cache 的工作方式 Web Cache 作为一种网页缓存技术,可以在用户访问网站服务器的任何一个中间网元上实现.根据 HTTP 协议的定义,在一次网页访问中,用户从客户端发出请求到网站服务器 ...
- Method, apparatus, and system for speculative abort control mechanisms
An apparatus and method is described herein for providing robust speculative code section abort cont ...
随机推荐
- redis 配置
一 Redis 支持写的指令 Redis大概的命令如下:set setnx setex appendincr decr rpush lpush rpushx lpushx linsert lset r ...
- MySql access denied for user错误
MySql access denied for user错误 | 浏览:2812 | 更新:2014-11-27 11:16 MySql access denied for user错误 方法/步骤 ...
- execve(file, argv, env)参数argv获取字符串个数
/* Copyright (C) 1999, 2000, 2002, 2003 Free Software Foundation, Inc. This file is part of the GNU ...
- hdu1521 排列组合(指数型母函数)
题意: 有n种物品,并且知道每种物品的数量ki.要求从中选出m件物品的排数. (全题文末) 知识点: 普通母函数 指数型母函数:(用来求解多重集的排列问题) n个元素,其中a1,a2, ...
- linux学习(2)
自从安装了虚拟机和各种工具软件之后,学习Linux的过程不断被打断,一直想把Ubuntu烧录到itop4412开发板里面去,却总是失败,感觉这个过程都加强我的抗打击能力了,现在来说说,对于一个第一次烧 ...
- CF731C. Socks[DFS 贪心]
C. Socks time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...
- UVA - 11584 Partitioning by Palindromes[序列DP]
UVA - 11584 Partitioning by Palindromes We say a sequence of char- acters is a palindrome if it is t ...
- HashSet和TreeSet
package com.wzy.list; import java.util.HashSet; import java.util.Iterator; import java.util.Set; imp ...
- Node webkit启动最大化窗口
<!DOCTYPE html> <html> <head> </head> <body> <p style=" ...
- Mysql数据库主从心得整理
管理mysql主从有2年多了,管理过200多组mysql主从,几乎涉及到各个版本的主从,本博文属于总结性的,有一部分是摘自网络,大部分是根据自己管理的心得和经验所写,整理了一下,分享给各位同行,希望对 ...