HTTP Cache 为什么让人很困惑

网上有很多关于 HTPP Cache 的知识,但个人感觉大部分讲的并不好,而这个主题对于 Web 开发者来说很重要,其实假如想全面了解相关知识,参考 MDNGoogle 开发者网站 这两篇文章就可以了。千万不要去看 RFC 文档,个人在看的时候非常费劲,最后放弃了。

本文算一个笔记,阐述其中的关键点,也会说明为什么这个主题不好讲(大部分写的不好的原因在于整体把控做的不好,或者说理解的比较片面)。

要区分理解 Private browser caches 和 Shared proxy caches

HTTP Cache 包括浏览器缓存和代理服务器缓存(比如 CDN),很多文章在描述的时候没有有效的区分这两者,所以会让人比较困惑。

浏览器缓存的服务架构可能是这样的:浏览器(Cache)=>服务器。
代理服务器缓存架构可能是这样的:浏览器=>CDN(Cache)=>源服务器。

不同的 HTTP Cache 解决的问题和使用的场景是不一样的。个人理解浏览器缓存主要是为了避免不必要的请求和大量的网络传输,而代理服务器缓存主要是为了让服务离用户更近更有效率(当然也解决了请求和网络传输)。

对于 Web 开发者来说,可能经常遇到的还是浏览器缓存,这篇文章主要说的也是此类缓存

而对于 HTTP Cache Header 指令(主要是 Cache-Control)来说,对于这两种类型的缓存,具体在使用上有不少的区别,需要仔细分辨。

浏览器行为是不可控的

HTTP Cache 是通过 HTTP Cache Header 指令来控制的,指令分为请求和响应指令,响应指令告诉浏览器应该做什么(当然浏览器可以不遵守),而响应指令也一定程度上控制未来可能的请求指令。

不过不同浏览器针对请求指令处理机制可能是不一样的,比如浏览器“回退动作”、“F5 动作”、“Ctrl +F5” 等动作会发出不一样的请求指令。

具体查看下面的图,通过这张图,就明白为什么“回退动作”,会从浏览器缓存获取数据了。

浏览器行为控制指令

使用 HTTP/1.1 标准的指令

HTTP 协议是一直演变的,不考虑浏览器版本和服务器的问题,尽量使用最新标准协议的头,因为假如混着理解,会让人很困惑,比如 Expires 和 Pargma 等指令都可以被 Cache-Control 指令替代了。

正确理解 Cache-Control 指令

这个指令是一个通用首部字段,就是说这个指令能够作为请求和响应指令,同时这个指令的参数也有多个,比如说其参数 max-age = 0 在请求和响应指令中分别代表什么?在理解的时候一定要分辨清楚。

进一步理解 Cache-Control 指令

理解了这个指令基本上就理解了 HTTP Cache,个人觉得这句话(Cache-Control directives control who can cache the response, under which conditions, and for how long)精确描述了这个指令。

它有三个含义:

(1)能否缓存(针对响应来说)

  • private:表示它只应该存在与浏览器缓存。
  • public:表示它可以缓存在浏览器或者 CDN 上。
  • no-cache:这个词很迷惑,不是代表不能使用缓存,而是代表在使用前必须到服务器上确认。
  • no-store:表示不允许被缓存。

(2)缓存多久(针对响应来说)

  • max-age= 秒,告知浏览器这个缓存的有效时间多少。

(3)revalidation(针对响应来说,就是条件检查)

  • must-revalidate:表示浏览器必须检查服务器,确认本地缓存是否有效,这个参数和请求参数 max-age = 0 有些类似。

这个指令形象的告诉浏览器,你是不是可以缓存这个对象,这个对象缓存时间是多少,是否在每次使用缓存的时候先确认下。

如何使用你的 Cache-Control 策略

对于一个开发者来说,如何设定Cache-Control 策略是门艺术,首先要明白资源是什么性质的,在此基础上定义 HTTP Cache 策略,Google 开发者网站的这张图形象的描述了策略。

Cache-Control 策略
  • 这个资源是否允许缓存?
  • 客户端每次使用缓存的时候需要去服务器校验吗?
  • 这个缓存是 Public 的还是 Private?
  • 缓存时间多少?
  • 资源标识符是什么(Etag)?

浏览器如何校验缓存

通过上面的描述,开发者明白了如何设置 HTTP Cache ,那么浏览器如何选择是否使用缓存呢?理解了这个会巩固理解 Cache-Control 策略。

这里面会增加两个指令,ETag 指令和 Last-modified,分别代表什么含义呢?ETag 表示资源的唯一性,假如这个值变化了代表资源更新了;Last-modified 表示资源最后的更新时间;

通过上面的图也可以发现,开发者可以在响应的时候输出这两个头信息。那这个指令代表什么意思呢?

当浏览器发现本地有缓存,且服务器指示没有必要每次使用前去确认,那么浏览器可以直接使用本地缓存。

当浏览器发现缓存已经过期了,那么这个时候可以选择重新去获取资源,但是有这么一种情况,服务器资源其实没有变化,那么为了减少带宽使用,服务器输出一个 304 HTTP 协议头,告诉浏览器,你继续使用你存储的缓存把。
问题来了,服务器怎么知道这个资源没有变化呢(从浏览器缓存生效的那时算),假如在第一次响应的时候输出了 Last-modified 头(表示资源的最后更新时间),那么客户端发现缓存失效的时候,在请求的时候会带上 if-Modified-Since(其实就是 Last-modified 的值)信息,服务器一看服务器上的资源最后更新时间小于或等于 if-Modified-Since 时间,就表示这个资源其实是新的,然后就发送一个 304 头。

如何通过 Nginx 来配置

大部分情况下,Nginx 会进行如下配置,但是需要明白含义,

location ~* \.(ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
   expires 10d;
}

expires 这个指定会输出如下的头:

Cache-Control:max-age=864000
Date:Tue, 28 Mar 2017 10:00:38 GMT
ETag:"5864a0ab-1e75"
Expires:Fri, 07 Apr 2017 10:00:38 GMT
Last-Modified:Thu, 29 Dec 2016 05:35:39 GMT

假如缓存没有过期就会一直使用,每次也不会去服务器校验,假如想每次请求资源的时候都确认下,可以使用以下指令:

location ~* \.(ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
    expires 10d;
    add_header Cache-Control "no-cache,must-revalidate,max-age=0";
}

动态程序如何控制

动态程序要负责所有的 HTTP Cache 头输出,还要自己计算 Etag,直接上代码看把:

<?php
$now = gmdate("D, d M Y H:i:s", time() ) . " GMT";
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;

if ($if_modified_since && $if_modified_since  >$now){
    header('HTTP/1.1 304 Not Modified');
    exit();
} else {

    $seconds_to_cache = 3600*24;
    $ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";

    header("Last-Modified: $ts");
    header("Cache-Control: no-cache, must-revalidate");
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 本篇文章篇幅比较长,先来个思维导图预览一下。 一、概述 1.计算机网络体系结构分层 2.TCP/IP 通信传输流 ...
    涤生_Woo阅读 54,930评论 24 557
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • 一、概念(载录于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434阅读 8,328评论 6 152
  • 本文内容大多参考《图解HTTP》一书 一. 认识代理服务器 所以讲缓存为什么要先扯代理服务器?别急,让我们看一下一...
    流光号船长阅读 1,898评论 0 10
  • 网络特有的延迟以及数据传输的成本,制约互联网快速获取Web资源。为此,HTTP协议引入缓存以空间换时间,使浏览器缓...
    大头8086阅读 3,056评论 2 12