DNS 与 HTTP 缓存实现方式(强制缓存与协商缓存)

Web 性能优化里”快”这件事,80% 的收益来自缓存。DNS 缓存解决”域名到 IP”的重复查询,HTTP 缓存解决”URL 到资源内容”的重复拉取。两者都有强制缓存和协商缓存两种思路:前者靠”算时间”跳过重复工作,后者靠”问一句”省下重复拉取的带宽。理解这两层缓存的实现方式,是定位”页面慢、版本不更新、CDN 命中率低”等问题的基础。

一、DNS 缓存的 8 种实现方式

DNS 查询从浏览器到根服务器之间存在多级缓存,命中任意一级都能节省一次网络往返。下面是工程里实际生效的 8 种实现方式,按从客户端到递归解析器再到权威的顺序展开。

  1. 浏览器 DNS 缓存:Chrome、Firefox、Edge 等浏览器内部维护,TTL 由浏览器策略决定(Firefox 默认 60 秒),可在 chrome://net-internals/#dns 查看;命中即返回 IP,毫秒级。
  2. 操作系统 DNS 缓存:Windows 由 DNS Client 服务维护,macOS 由 mDNSResponder 维护,Linux 由 systemd-resolved 或 nscd 维护,所有应用共享,跨进程复用,TTL 取权威服务器下发的值。
  3. hosts 文件静态映射:/etc/hosts 或 C:\Windows\System32\drivers\etc\hosts,没有 TTL 概念,文件里写什么就一直生效;常用于本地开发、内网域名、临时屏蔽广告。
  4. 路由器 DNS 缓存:家用与企业路由器对局域网内所有设备的查询结果做缓存,重启即清空;多设备共享,命中率最高。
  5. 本机/局域网自建递归解析器:企业内网部署 unbound、dnsmasq 等本地递归解析器,可自定义转发策略、过滤广告域名、统一记录查询日志。
  6. 公共递归解析器缓存:Google 8.8.8.8、Cloudflare 1.1.1.1、Quad9 9.9.9.9 等公共 DNS 服务,覆盖全球数亿终端,命中率极高;支持 DoH/DoT 加密查询。
  7. ISP 运营商递归解析器缓存:运营商自建解析器对本网用户查询结果做缓存,是大多数家庭网络真正命中的那一层;改动 DNS 记录时”全球生效慢”多由它造成。
  8. 权威 DNS 服务器自身缓存:一些权威服务商(如 Cloudflare DNS、Route 53)会在边缘节点对热记录做缓存,进一步降低对源站的查询压力。

1.1 DNS 缓存命中顺序示例

浏览器输入 www.example.com
   ↓ ① 浏览器 DNS 缓存(命中即返回)
   ↓ ② 操作系统 DNS 缓存
   ↓ ③ hosts 文件
   ↓ ④ 路由器 DNS 缓存
   ↓ ⑤ 本地/ISP/公共递归解析器
   ↓ ⑥ 根 → TLD → 权威解析器(只有全部 miss 才走完)

冷查询通常 20-120 ms;命中任一层缓存都压到 10 ms 以内,浏览器自身命中可压到 1 ms 内。一张含 30 个第三方域名的页面,DNS 耗时差距常常是 200 ms 级别。

1.2 TTL 与生效延迟

DNS 记录本身带 TTL,TTL 越大缓存复用率越高,但解析记录变更后”全球生效”时间也越长。迁移前临时把 TTL 调到 300 秒是常规做法,迁移结束后再调回 86400 秒这类长值。

二、HTTP 缓存的两种核心策略

HTTP 缓存分两层:先看强制缓存是否生效,没命中再走协商缓存。两者是递进而非并列关系。

2.1 强制缓存:本地判断,不发请求

强制缓存的核心是”浏览器自己决定资源是否过期”,完全不与服务器通信。判断依据是两个响应头:

  • Cache-Control(HTTP/1.1,主流):max-age=N 表示从响应返回时刻起 N 秒内有效,相对时间,不受客户端本地时钟影响;
  • Expires(HTTP/1.0,已基本被取代):Wed, 28 May 2026 10:00:00 GMT 形式,写绝对时间,受客户端时差影响。

两者并存时 Cache-Control 优先级更高。命中后浏览器状态栏显示 200 (from disk cache) 或 200 (from memory cache)。

Cache-Control 还提供 no-cache(跳过强制缓存,强制走协商)、no-store(完全禁用缓存)、public(允许 CDN 等中间节点缓存)、private(仅浏览器可缓存)、immutable(声明期内不变化,避免刷新重新校验)等指令,用于精细控制策略。

2.2 协商缓存:服务器验证,节省带宽

强制缓存过期后浏览器不是直接拉新资源,而是把缓存标识发给服务器,由服务器判断是否还能用旧副本。服务器返回两种结果:

  • 304 Not Modified:资源没变,浏览器继续用本地副本,响应体通常只有几百字节;
  • 200 OK + 新资源:资源已更新,浏览器更新本地缓存。

协商缓存有两条独立路径,可单独使用也可同时配置:

  1. Last-Modified / If-Modified-Since:服务器返回资源的最后修改时间,浏览器下次请求带上;服务器对比时间判断是否更新。精度只到秒,文件保存但未改内容时会误判。
  2. ETag / If-None-Match:服务器根据资源内容算一个唯一哈希(强 ETag 用 ETag: "abc",弱 ETag 用 ETag: W/"abc"),浏览器下次带上对比;精度高,能识别秒级内修改。

两者并存时 ETag 优先级更高,因为 ETag 是按内容计算的,Last-Modified 是按文件元信息计算的;前者更贴合”内容是否真的变了”这个核心问题。

2.3 强制 vs 协商:一张表看完

维度 强制缓存 协商缓存
是否发请求 否 是(仅头部)
状态码 200 (from cache) 304 Not Modified / 200 OK
控制字段 Cache-Control、Expires ETag/If-None-Match、Last-Modified/If-Modified-Since
适用协议 HTTP/1.0、HTTP/1.1 HTTP/1.0、HTTP/1.1
精度 时间粒度(秒级) ETag 可字节级
典型场景 静态资源(图片、CSS、JS) 频繁更新或需精确验证的内容
失效后行为 进入协商缓存 服务器返回 200 + 新资源

三、一次完整 HTTP 缓存决策流程

浏览器处理一个 URL 时按下面顺序判断,命中任一分支即停止:

  1. 缓存里有吗?没有则直接发请求拿资源;
  2. 有 → 看 Cache-Control(或 Expires)是否过期,没过期直接用本地副本;
  3. 过期 → 看是否有 ETag 或 Last-Modified,有则带 If-None-Match / If-Modified-Since 问服务器;
  4. 服务器返回 304 → 继续用本地副本;返回 200 → 用新资源并更新缓存。
GET /app.css HTTP/1.1
Host: example.com
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Modified-Since: Wed, 27 May 2026 10:00:00 GMT
HTTP/1.1 304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: max-age=3600

304 响应只回响应头、不回响应体,这是它能省带宽的关键。注意 Ctrl+F5 这种”强制刷新”会绕过强制缓存和协商缓存,请求头里不带任何缓存标识,等同于把缓存层整层掀掉。

四、典型场景的缓存策略

把强制与协商两层组合用好,是缓存设计的核心。下面是经过大量线上项目验证的三种典型组合:

  1. 带哈希文件名的静态资源(如 app.a5d7f8e3.js):HTML 不强制缓存,每次走协商;JS/CSS/图片用 Cache-Control: max-age=31536000, immutable 强制缓存一年,更新靠改文件名触发。
  2. 接口数据:用 Cache-Control: no-cache 跳过强制缓存,由 ETag/Last-Modified 走协商,命中后只回 304 不回响应体;适合字典、配置这类低频变更数据。
  3. 敏感信息:用 Cache-Control: no-store 完全禁用缓存,每次都从服务器拉;常见于支付、登录态、个人隐私类响应。
# Nginx 静态资源缓存策略示例
location /static/ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
location /api/ {
    add_header Cache-Control "no-cache";
    etag on;
    if_modified_since exact;
}

五、落地时的易错点

缓存策略看着简单,但生产环境里翻车的姿势很多:

  • 只配 Cache-Control 不配 ETag:强制缓存过期后无法走协商,每次都要重新下载整资源;
  • HTML 配了长 max-age:版本更新后用户长时间看不到新页面;
  • CDN 上 Vary 配置缺失:移动端/桌面端响应错乱,命中率被人为降低;
  • DNS 缓存未刷新:迁移后部分用户访问老 IP 长达 TTL 周期,提前调低 TTL 能显著缩短”全球生效”时间;
  • 304 响应被错误缓存:某些代理会把 304 响应缓存为 200 资源,导致资源看起来”不变”。

常见问题(FAQ)

Q1:DNS 缓存多久清一次合适?

浏览器缓存由浏览器策略决定,操作系统与递归解析器按权威返回的 TTL 走。生产环境变更 DNS 记录前,先把 TTL 调小到 300 秒再改,可避免长时间”全球不生效”。

Q2:强制缓存和协商缓存哪个先判断?

强制缓存先判断。命中直接用本地副本,状态码 200 (from cache);未命中或被声明为 no-cache 才进入协商阶段,由服务器返回 304 或 200。

Q3:ETag 会不会泄露文件内容?

强 ETag 通常是内容哈希,但默认不暴露内容明文;如需更严格控制,可改为版本号或时间戳作为标识,或改用弱 ETag(W/ 前缀)容忍微小差异。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
小其圈圈的头像小其圈圈运营团队

相关推荐

返回顶部