知

模块二 · 工程化与网络

第 7 / 14 节21 分钟阅读型

网络与缓存:一个请求要付出多少成本

一次请求的成本由 DNS、连接、TLS、传输与解析共同决定。这一节讲清 HTTP 版本差异、缓存策略、CDN、跨域与安全,以及可落地的优化手段。

读完这一节,你能做到

  1. 说清 HTTP/1.1、HTTP/2、HTTP/3 各自解决什么问题。
  2. 写出正确的缓存策略,而不是只会加 no-cache。
  3. 判断跨域问题出在预检、凭证还是响应头。
  4. 用 preload/preconnect 优化关键请求链。

一、HTTP 版本演进

版本传输层并发主要问题
HTTP/1.1TCP每域名 6 个左右连接队头阻塞、头部冗余
HTTP/2TCP单连接多路复用TCP 层队头阻塞
HTTP/3QUIC(UDP)多路复用、0-RTT兼容性与中间设备

HTTP/2 的头部压缩(HPACK)与多路复用,解决的是应用层队头阻塞;但 TCP 丢包仍会阻塞所有流,HTTP/3 用 QUIC 把流独立开,才解决传输层问题。

理解这条演进,就能回答“为什么域名分片在 HTTP/2 下反而有害”——多连接会破坏多路复用与压缩收益。

二、缓存:先问三个问题

  1. 这个资源能不能缓存?(敏感数据不能)
  2. 缓存多久?(强缓存)
  3. 过期后怎么验证?(协商缓存)
指令作用
Cache-Control: max-age=31536000, immutable一年强缓存,适合带哈希的静态资源
Cache-Control: no-cache可以缓存,但每次都要验证
Cache-Control: no-store完全不缓存,适合敏感数据
ETag + If-None-Match协商缓存,未变返回 304
Last-Modified + If-Modified-Since时间戳验证,精度较低

最常见的错误是把 no-cache 当成“不缓存”。它其实是“缓存但必须验证”,真正不缓存是 no-store。

内容哈希 + 长强缓存是静态资源的最佳实践:文件内容变,哈希变,URL 变,自然绕过缓存。

三、CDN 与边缘缓存

CDN 把资源放到离用户更近的边缘节点,减少 RTT 与回源。关键配置:

  • 缓存键:默认按 URL,忽略无关查询参数,避免缓存碎片。
  • 回源策略:边缘未命中时回源,回源带宽是成本大头。
  • 刷新与预热:发布后刷新边缘缓存,大促前预热。
  • 动态加速:API 走动态加速,减少跨地域延迟。

CDN 不是“加了就快”:如果缓存命中率低,收益会被回源抵消。

四、跨域与安全

跨域是浏览器同源策略的限制,不是服务器拒绝。解决方式:

  • CORS:服务端返回 Access-Control-Allow-Origin 等头;非简单请求会先发 OPTIONS 预检。
  • 凭证:带 Cookie 时需要 credentials: 'include',且服务端不能返回 *。
  • 代理:开发环境用 devServer 代理,生产用同域反向代理。

安全相关:

头作用
Content-Security-Policy限制脚本、样式、图片来源
X-Content-Type-Options: nosniff禁止 MIME 嗅探
Set-Cookie: SameSite=Lax限制跨站携带 Cookie
Strict-Transport-Security强制 HTTPS

CORS 是“读取限制”,不是“请求限制”:请求可能已经到达服务端,只是响应被浏览器拦截。

XSS、CSRF 与 CSP

先分清两类攻击:

  • XSS:把恶意脚本注入你的页面,在用户浏览器里执行。
  • CSRF:借用用户已登录的身份,在用户不知情时发起请求。

一个往里塞代码,一个借身份发请求,防御思路完全不同。

XSS 的三种类型

类型注入点例子
存储型数据存进数据库后再渲染出来评论区写入 <script>
反射型参数直接回显到页面搜索词原样输出到 HTML
DOM 型前端把不可信数据写进 DOMinnerHTML = location.hash

防御三层

  1. 输出转义:渲染用户内容时转义 < > & " '。React 的 JSX 默认转义,Vue 的 {{ }} 也是;危险的是 v-html 和 dangerouslySetInnerHTML。
  2. CSP:限制脚本只能从白名单加载,即使被注入也无法执行外链脚本。
  3. 输入校验:不能替代转义,但能减少攻击面。

CSRF 的三个条件:用户已登录、攻击者能构造请求、浏览器会自动带上 Cookie。防御就是打断其中任一条件:

手段原理
SameSite=Lax/Strict跨站请求不携带 Cookie
CSRF Token请求需带上服务端下发的随机值
校验 Origin / Referer拒绝非本站来源
敏感操作改 POST + 二次确认提高攻击成本

CSP 怎么配

最小的可用配置:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'

三条纪律:不用 unsafe-inline(改用 nonce 或 hash)、不用 unsafe-eval、先用 report-only 观察一段时间再上线拦截。

面试一句话:「XSS 是注入,靠转义与 CSP 防;CSRF 是冒用身份,靠 SameSite 与 Token 防。」

五、关键请求链优化

<link rel="preconnect" href="https://api.example.com" crossorigin />
<link rel="dns-prefetch" href="//cdn.example.com" />
<link
  rel="preload"
  href="/fonts/main.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

三条原则:

  1. 只对“首屏必须且发现晚”的资源用 preload。
  2. preconnect 只给确定马上要用的域名。
  3. prefetch 用于下一页,优先级最低,不要抢首屏带宽。

六、常见坑

  1. 把 no-cache 当成不缓存。
  2. 静态资源不带哈希却设置长缓存,发布后用户看到旧版本。
  3. 带 Cookie 的跨域请求返回 *,浏览器直接拒绝。
  4. 预检请求没缓存,每次请求都多一次 RTT。
  5. CDN 缓存键包含随机参数,命中率极低。

七、动手任务:自写 → AI 写 → 对比 → 取舍

四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。 完整动作与记录模板见《AI 协作与代码评审》。

① 自己写

  1. 用 DevTools 看一次请求的缓存命中情况,判断是强缓存还是 304。
  2. 给静态资源配置内容哈希 + 长缓存,验证发布后的更新。
  3. 用 OPTIONS 预检观察一次跨域请求,检查 Access-Control-Max-Age。

② 让 AI 写同一题

把第 2 题(缓存策略设计)交给 AI,补一句 说清强缓存与协商缓存分别用在什么资源上。

③ 对比要盯的点

  • Cache-Control 与 ETag 分工它说对了吗?前者管强缓存有效期,后者管协商。
  • 它有没有区分“没有缓存”和“缓存但需验证”?
  • stale-while-revalidate 它提了吗?这是静态资源常用的折中。

④ 一句话取舍

发版之后,它这套策略会让用户多久看到新资源?

八、自测

  1. HTTP/2 与 HTTP/3 分别解决什么队头阻塞?
  2. 强缓存与协商缓存的判断依据是什么?
  3. no-cache 与 no-store 的区别?
  4. 带凭证的跨域为什么不能用 *?
  5. preload 与 prefetch 的区别?
  6. XSS 与 CSRF 的防御思路分别是什么?

参考答案要点:应用层 vs 传输层;是否发请求;验证 vs 不存;凭证与通配冲突;关键资源 vs 空闲预取;XSS 靠输出转义与 CSP,CSRF 靠 SameSite、Token 与来源校验。

附录:高频追问

问:304 会带响应体吗? 不会,只有响应头,节省带宽。

问:ETag 和 Last-Modified 谁优先? 服务端可同时返回,浏览器优先用 ETag,因为更精确。

问:为什么静态资源要用内容哈希? 文件名随内容变化,可以放心设置长强缓存,发布即失效。

问:CORS 预检什么时候触发? 非简单请求(自定义头、PUT/DELETE、非表单 content-type)会先发 OPTIONS。

问:SameSite=Lax 影响什么? 限制跨站请求携带 Cookie,降低 CSRF 风险,但可能影响第三方登录回跳。

问:HTTP/2 的服务端推送为什么被弃用? 推送难以与缓存协作,容易推了不需要的资源,现多用 103 Early Hints 替代。

问:CDN 回源失败怎么办? 配置多源与降级,边缘可返回 stale 缓存,避免直接 5xx。

问:如何判断缓存配置是否正确? 看响应头的 Cache-Control、Age、X-Cache,以及 DevTools 的 “from disk/memory cache”。

三种缓存失效场景

  1. 发布后 HTML 命中旧缓存,引用了不存在的旧 chunk。解法:HTML 用 no-cache 或短缓存。
  2. CDN 缓存键包含随机参数,命中率极低。解法:配置忽略无关 query。
  3. 灰度发布时同一 URL 返回不同内容,边缘缓存串味。解法:按版本或灰度标识区分缓存键。

一次跨域排查

现象:接口在 Postman 正常,浏览器报 CORS。排查顺序:

  1. 看请求是否发了 OPTIONS,预检返回什么。
  2. 看响应头是否包含 Access-Control-Allow-Origin,是否与 Origin 完全一致。
  3. 带 Cookie 时,检查 Allow-Credentials 与 Allow-Origin 是否冲突。
  4. 检查网关/反代是否覆盖了服务端返回的头。

CORS 报错必须看响应头,不能只看控制台结论。

缓存策略速查

资源策略
带 hash 的 JS/CSS一年强缓存 + immutable
HTMLno-cache(每次协商)
图片/字体长缓存,文件名带版本
API 数据按业务定,敏感数据 no-store

这张表能覆盖 80% 的场景,剩下的是灰度与多租户的缓存键设计。

CDN 命中率怎么算

命中率 = 边缘命中次数 ÷ 总请求次数

命中率低通常有三个原因:缓存键太细(带随机参数)、TTL 太短、资源变体太多。先看命中率,再谈回源优化;命中率不上去,加带宽只是花钱。缓存是性能的第一杠杆,先命中,再优化;命中率决定收益。先命中,后回源。

配套题目

★ 越多越该先背;点题目可看完整答案与深入版。

提示:读完可以顺手做几道对应题目,检验是否真的理解