[实战验证] http缓存(无代理服务器)

本文深入探讨HTTP缓存机制,包括Cache-Control与Expires属性的作用及优先级,验证无代理服务器环境下缓存行为,分析资源缓存、协商缓存及直接获取资源的条件。

一. 背景

最近看了不少关于客户端缓存机制的文章,大概弄明白了整个原理,但是对于中间一些细节一直有点迷糊,下面的内容是自己做的验证


二. 准备工作

http缓存的基本情况可以看看下这些文章

前端之浏览器缓存,一次搞定

前端也要懂Http缓存机制

常见的影响缓存的配置有以下几个:
  1. 原服务器的Expires
  2. 原服务器的Cache-Control
  3. 代理服务器(nginx)Expires
  4. 代理服务器(nginx)proxy_cache_valid
  5. 代理服务器(nginx)proxy_cache_path中的inactive

当前这篇会先验证无代理服务器的情况,有代理的情况放在之后验证

配置说明

原服务器的Cache-Control

名称说明
private客户端可以缓存,代理服务器不可以缓存
public客户端和代理服务器都可以缓存
max-age=t缓存内容将在t秒后失效
no-cache使用协商缓
no-store不使用缓存

原服务器的Expires

这是http 1.0的属性,现在应该用的少了;该属性设置的是一个过期时间,过期时间内命中强缓存;过期时间外,协商缓存


三. 验证

以下是我准备验证的问题:

  1. 原服务器的Cache-Control不同属性的实际情况
  2. 原服务器的Expires的实际情况
  3. 不使用代理服务器的情况下,原服务器的ExpiresCache-Control同时存在,那个优先级高

[1-24 更新] 增加一个验证:

  1. 如果上述头都未设置,缓存情况是怎样的?

1. 首先是无代理服务器的情况

验证方式是使用node的express启动一个服务

const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
const port = 3030;

app.use(express.static(path.resolve(__dirname, './')));

app.get('/index', (req, res) => {
  const html = fs.readFileSync(path.resolve(__dirname, './index.html'), 'utf-8');
  res.send(html)
})

// cache验证
app.all('*', (req, res, next) => {
  // res.header('Cache-Control', 'private');
  // res.header('Cache-Control', 'public');
  res.header('Cache-Control', 'max-age=20');
  // res.header('Cache-Control', 'no-cache');
  // res.header('Cache-Control', 'no-store');
  next();
})

const questions = [
  {
    id: '000',
    name: 'Rose'
  },
  {
    id: '111',
    name: 'Jack'
  }
]

app.get('/api/data', (req, res) => {
  res.status(200);
  res.json(questions);
})

// 监听端口
app.listen(port, () => {
  console.log(`success listen at ${port}`);
})

复制代码
1.1 Cache-Control的各属性验证

ps:因为不存在代理服务器,所以public和private的区别现在是看不出来的,我们之后和有代理的情况一起验证

// 单独验证Cache-Control
app.all('*', (req, res, next) => {
  res.header('Cache-Control', 'max-age=30');
  // res.header('Cache-Control', 'no-cache');
  // res.header('Cache-Control', 'no-store');
  next();
})
复制代码

以下验证可以得出结论:

  1. max-age未过期 -> 命中强缓存
  2. max-age过期 -> 资源未修改 -> 命中协商缓存
  3. max-age过期 -> 资源已修改 -> 服务器获取资源
  4. no-cache -> 资源未修改 -> 命中协商缓存
  5. no-cache -> 资源已修改 -> 服务器获取资源
  6. no-store -> 服务器获取资源
Cache-Control请求延迟时间资源是否改变If-None-Match(request)ETag(respanse)Status Code结论
max-age=3010sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OK (from disk cache)强缓存未过期,命中强缓存
max-age=3060sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo"200 OK强缓存过期,资源被修改,重新从服务器获取资源
max-age=3010sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OK (from disk cache)强缓存未过期,命中强缓存
max-age=3060sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"304 Not Modified强缓存过期,资源未修改,命中协商缓存
no-cache30sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo"200 OK资源被修改,未命中协商缓存,从服务器获取资源
no-cache30sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"304 Not Modified资源未修改,命中协商缓存
no-store30sW/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo"200 OK不使用缓存,直接从服务端获取资源
no-store30sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OK不使用缓存,直接从服务端获取资源
1.2 Expires验证

ps1:如果这个Expires字段后端没有处理过的话,返回的应该是GMT的标准时间Wed, 23 Jan 2019 09:52:55 GMT,也就是格林威治的标准时间;而我们一般使用的是本地时间Wed Jan 23 2019 18:06:15 GMT+0800 (中国标准时间),所以要做一下适当的处理

ps2:我是在chrome上用localhost上测试的,但不知道为什么设置Expires后,不管过没过期,不管有没有同时设置Cache-ControlStatus Code状态一直是304的,听说好像是因为用了localhost的关系,这个与线上并不一定完全一致;这里关于Expires的测试我是加了nginx代理了之后的结果,不过代理的缓存没有设置

ps3:Expires的结果就仅供参考吧,以上

const moment = require('moment');
// cache验证
app.all('*', (req, res, next) => {
  res.header('Expires', getGLNZ());
  next();
})
// 转换格林威治时间
function getGLNZ() {
  return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
}
复制代码

以下验证可以得出结论:

  1. Expires未过期 -> 命中强缓存
  2. Expires过期 -> 资源未修改 -> 命中协商缓存
  3. Expires过期 -> 资源已修改 -> 服务器获取资源
Expires请求延迟时间资源是否改变If-None-Match(request)ETag(respanse)Status Code结论
当前时间+30s10sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OK (from disk cache)强缓存未过期,命中强缓存
当前时间+30s60sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"304 Not Modified强缓存过期,资源未修改,命中协商缓存
当前时间+30s10sW/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo"200 OK (from disk cache)强缓存未过期,命中强缓存
当前时间+30s60sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo"200 OK强缓存过期,资源被修改,重新从服务器获取资源
1.3 Expires与Cache-Control优先级

结论:Cache-Control优先级比Expires优先级高

const moment = require('moment');
// cache验证
app.all('*', (req, res, next) => {
  res.header('Cache-Control', 'max-age=60');
  // res.header('Cache-Control', 'no-cache');
  // res.header('Cache-Control', 'no-store');
  res.header('Expires', getGLNZ());
  next();
})

function getGLNZ(){
  return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
}
复制代码

以下验证可以看出,在Cache-ControlExpires同时设置的情况下,Expires是失效的

ExpiresCache-Control请求延迟时间资源是否改变If-None-Match(request)ETag(respanse)Status Code结论
当前时间+30smax-age=6010sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OK (from disk cache)Expires与Cache-Control都未过期,命中强缓存
当前时间+30smax-age=6040sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OK (from disk cache)Expires过期,Cache-Control未过期,命中强缓存
当前时间+30smax-age=60100sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"304 Not ModifiedExpires与Cache-Control都过期,资源未修改,命中协商缓存
当前时间+30sno-cache10sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"304 Not ModifiedExpires未过期,但是命中协商缓存
当前时间+30sno-cache60sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"304 Not ModifiedExpires过期,命中协商缓存
当前时间+30sno-store10sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OKExpires未过期,直接获取服务端资源
当前时间+30sno-store60sW/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OKExpires过期,直接获取服务端资源
1.4 Expires与Cache-Control都未设置

以下验证结论:

  1. If-None-MatchETag相同 -> 协商缓存
  2. If-None-MatchETag不相同 -> 服务器获取资源
资源是否改变If-None-Match(request)ETag(respanse)Status Code结论
W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"200 OK服务器获取资源
W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"W/"37-W4vHvR7+YshQyC9DoyT14egVUqo"304 Not Modified命中协商缓存

四. 总结:

1. 只设置Cache-Control
  • max-age未过期 -> 命中强缓存
  • max-age过期 -> 资源未修改 -> 命中协商缓存
  • max-age过期 -> 资源已修改 -> 服务器获取资源
  • no-cache -> 资源未修改 -> 命中协商缓存
  • no-cache -> 资源已修改 -> 服务器获取资源
  • no-store -> 服务器获取资源
2. 只设置Expires
  • Expires未过期 -> 命中强缓存
  • Expires过期 -> 资源未修改 -> 命中协商缓存
  • Expires过期 -> 资源已修改 -> 服务器获取资源
3. Cache-ControlExpires同时存在
  • 只有Cache-Control生效
4. Cache-ControlExpires都未设置
  • 对比request中的If-None-Matchresponse中的ETag
  • If-None-MatchETag相同 -> 协商缓存
  • If-None-MatchETag不相同 -> 服务器获取资源
  • 感觉实际上就是走的Cache-Control no-cahce

以上,验证了无代理服务器的情况下,http缓存常用的两个配置的结果;下一篇验证加了代理服务器的情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值