http 304原理

http 常见的状态码有很多,200,302,303,304,403,500等,其中要数304在面试中最常被问到。初学者碰到304,可能只是知道他是用于缓存,却并不知道其中的原理。下面我们来了解一下304整个的运行机制。

缓存运行机制

首先我们先思考一个问题,浏览器如何判断一个资源是否需要缓存?

可能你已经知道了,就是用请求头来控制的。

通知缓存

在 http1.1 中我们通过请求头中的 Cache-Control 来告诉浏览器缓存机制。

作为响应头其中可选的值有:

字段 作用
public 表示任何情况下都得缓存该资源
Private=[=”field-name”] 表明返回报文中全部或部分仅开放给某些用户作缓存(服务器指定的,如代理服务器等),其他用户则不能缓存这些数据
no-cache 不直接使用缓存,要向服务器发起新鲜度校验
no-store 所有内容不会被缓存
no-transform 告知客户端缓存文件时不得对实体数据做任何改变
only-if-cached 告知代理服务器,如果客户端需要的数据,代理服务器有,代理服务器不用想原服务器发送请求
must-revalidate 资源一定是向员服务器去验证请求,若请求失败会返回504
proxy-revalidate 与must-revalidate类似
max-age=delta-seconds 告知客户端在 delta-seconds 内资源是新鲜的,不用向服务器发起请求
s-maxage=delta-seconds 和max-age一样,但仅应用于共享缓存(如代理)
cache-extension 自定义拓展值

其中常用的主要是 max-age。

1
Cache-Control: max-age=100

这个请求头的意思是告诉浏览器该资源在100秒内都是新鲜的,如果未过期,则不用向服务器发送请求,直接读取缓存,在 chrome 下我们可以看到表现为 200 from cache,若过期了,则会重新像服务器发起请求。

缓存验证

现在我们已经知道了如何通知浏览器做缓存了,但是这里还有一个问题,缓存到期了,浏览器重新请求资源,但是如果资源在这段时间并没有发生修改,又重新把资源发送了一遍,如果资源又比较大,这就造成很大的宽带和时间的浪费。

304 就是用来告诉浏览器,资源还没更新过,我就不传过去了,你直接用缓存就可以了。

接着问题又来了,如何验证资源是否被修改过呢。

http 1.1 新增了几个首部字段来解决这个问题。

Last-Modified

服务器将资源返回给浏览器的时候,会将资源的最后修改时间加在请求头上返回给浏览器。

1
Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT

浏览器接收之后,会在下一次的请求把信息附带上,到服务器去做检查,若与服务器的修改时间一直,则说明资源没有被修改过,直接返回304即可。

浏览器会通过if-modified-since将信息发送给服务器

1
if-modified-since: Sun, 18 Mar 2018 13:01:31 GMT

Last-Modified 也可能会出现一个问题,如果服务器上,一个资源被修改了,但是实质上内容根本没有发生变化,也会重新发送整个资源给浏览器。

ETag

为了解决上面这个问题,Http1.1还加入了ETag这个首部。服务器通过某种算法,给资源计算出一个唯一的表示符,并在响应头上返回给浏览器。

浏览器会在下一次请求的时候,通过If-None-Match将信息发送给服务器

1
If-None-Match: "sdfsfedc8-2132"

如果匹配上了则直接返回304即可,没匹配上则需要重新发送资源。

需要注意的是,如果这两个字段被同时使用了,需要他们两个同时验证通过才会返回304,如果其中一个没验证通过,都会重新发送资源