Skip to content

04 · HTTP / HTTPS 与缓存 ​

传输层保证"数据能到",应用层决定"数据长什么样、能不能被缓存、安不安全"。

一句话本质 ​

HTTP 是一问一答的文本协议:客户端说"我要什么",服务器说"给你 / 没有 / 你去别处拿"。HTTPS 就是在它和 TCP 之间插了一层加密。

生活类比:点餐与取餐 ​

HTTP 概念餐厅
请求方法GET = "给我看看菜单";POST = "我下单";PUT = "把这道菜换成新的";DELETE = "退掉"
状态码200 上菜了;301 换到隔壁店了;404 没这道菜;500 后厨炸了
请求头忌口说明(我要微辣、不要香菜)
响应头出餐说明(这道菜 10 分钟内吃完、保质期到明天)
强缓存"这道菜你自己留着,2 小时内别再点"
协商缓存"你上次点的那份还在,还是老样子吗?" → "没变,用你那份"
HTTPS菜单和订单都装进密封袋,中间人看不到

核心机制 ​

一、报文结构 ​

请求:
  GET /index.html?page=2 HTTP/1.1        < 请求行:方法 + 路径 + 版本
  Host: example.com                       < 请求头
  User-Agent: Chrome/120
  Accept: text/html
  Cookie: sid=abc123
                                          < 空行(必须有)
  (GET 通常没有 body;POST 有)

响应:
  HTTP/1.1 200 OK                         < 状态行:版本 + 状态码 + 短语
  Content-Type: text/html; charset=utf-8   < 响应头
  Content-Length: 1024
  Cache-Control: max-age=3600
                                          < 空行
  <html>...</html>                        < 响应体

二、方法:幂等与安全(高频) ​

方法语义安全幂等有无 body
GET获取资源✅✅无
HEAD只要响应头✅✅无
POST提交数据 / 创建❌❌有
PUT全量替换❌✅有
PATCH局部更新❌❌有
DELETE删除❌✅无

安全 = 不改变服务器状态(只读)。 幂等 = 执行 1 次和执行 N 次,服务器状态相同。

为什么重要:网络会重试。GET/PUT/DELETE 重试没问题,POST 重试会重复下单——这就是"支付接口必须用幂等键"的原因。

三、状态码:记住"段位" ​

1xx  信息     100 Continue(我先发个头,你确认再发 body)
2xx  成功     200 OK / 201 Created / 204 No Content
3xx  重定向   301 永久(浏览器会缓存,下次直接跳)
              302 临时(每次都要问)
              304 未修改(协商缓存命中,用本地副本)
4xx  客户端错 400 参数错 / 401 没认证 / 403 没权限 / 404 不存在 / 429 太频繁
5xx  服务端错 500 内部错误 / 502 网关收到上游的坏响应 / 503 服务不可用 / 504 网关超时

两个易混点:

  • 401 vs 403:401 是"你没登录"(不知道你是谁),403 是"知道你是谁,但你没权限"。
  • 502 vs 504:502 是上游返回了非法响应(进程崩了、协议错),504 是上游没响应(超时)。502 常见于上游进程挂了,504 常见于上游处理太慢。

四、HTTP 版本演进(一条主线:不断减少往返) ​

HTTP/1.0  每个请求一个 TCP 连接,用完就关
          +- 问题:每次都要三次握手,太慢
              v
HTTP/1.1  ① 持久连接(keep-alive),复用 TCP 连接
          ② 管道化(pipelining):可以连着发多个请求,但**响应必须按序返回**
             +- 问题:队头阻塞,一个慢请求卡住后面全部
          ③ Host 头(支持虚拟主机,一个 IP 多个域名)
          ④ 分块传输(chunked)、断点续传(Range)
              v
HTTP/2    ① 二进制分帧(不再是文本)
          ② 多路复用:一个 TCP 连接上跑多个流,每个流有自己的 ID
             +- 解决了 HTTP 层的队头阻塞
          ③ 头部压缩(HPACK,用静态表 + 动态表,避免重复传相同的头)
          ④ 服务器推送(Server Push,实际用得少,已被废弃)
             +- 问题:底层还是 TCP,TCP 丢包会阻塞所有流
              v
HTTP/3    基于 QUIC(跑在 UDP 上)
          ① 每个流独立排序,彻底解决队头阻塞
          ② 0-RTT / 1-RTT 建连(TLS 1.3 集成在 QUIC 里)
          ③ 连接迁移(换网络不用重连,靠 Connection ID 而不是四元组)

面试必答的一句话:HTTP/2 解决的是应用层队头阻塞,没解决 TCP 层队头阻塞;HTTP/3 用 QUIC 才彻底解决。

五、缓存:两道关卡 ​

浏览器要请求一个资源
   v
① 强缓存:本地有,且没过期?
   Cache-Control: max-age=3600      (相对时间,优先)
   Expires: Wed, 21 Oct 2026 ...    (绝对时间,会被本地时间影响,已过时)
   +- 没过期 > 直接用本地,**不发请求**(状态码显示 200 from disk cache)
   +- 过期了 v
② 协商缓存:带上标识问服务器"变了吗"
   If-None-Match: "abc123"   < 对应 ETag(内容哈希,精确)
   If-Modified-Since: ...    < 对应 Last-Modified(文件修改时间,精度只到秒)
   +- 没变 > 304 Not Modified,**不传 body**,用本地副本
   +- 变了 > 200,返回新内容

为什么 ETag 优先于 Last-Modified:

  • Last-Modified 只精确到秒,一秒内改两次识别不出来。
  • 文件被 touch 但内容没变,Last-Modified 会变,导致不必要的重传。
  • ETag 是内容哈希,精确。

Cache-Control 的关键值:

值含义
max-age=3600强缓存 1 小时
no-cache可以缓存,但每次都要协商(不是"不缓存"!)
no-store完全不缓存(敏感数据)
private只允许浏览器缓存,CDN 不许
public允许 CDN 等中间代理缓存
immutable内容永不改变,连协商都省了(配合带哈希的文件名)

注意 no-cache 的坑:名字叫"不缓存",实际是"缓存但必须校验"。真正不缓存是 no-store。这是面试常见陷阱。

六、HTTPS:TLS 握手做了什么 ​

HTTPS = HTTP + TLS。TLS 要解决三件事:机密性(加密)、完整性(防篡改)、身份认证(防冒充)。

TLS 1.2 握手(简化,2-RTT):
① 客户端 > 服务器:ClientHello(支持的加密套件、随机数 A)
② 服务器 > 客户端:ServerHello(选定的套件、随机数 B)+ 证书
③ 客户端验证证书(查 CA 签名、查域名、查有效期、查吊销状态)
④ 客户端用证书里的公钥加密"预主密钥"发给服务器
⑤ 双方用 随机数A + 随机数B + 预主密钥 算出相同的会话密钥
⑥ 之后用对称加密通信

TLS 1.3(1-RTT,甚至 0-RTT):
  合并了握手轮次,删掉了不安全的算法(RSA 密钥交换、CBC 模式等)

为什么用"非对称 + 对称"混合:

  • 非对称加密(RSA/ECDHE):安全但慢(慢几百倍),只用来协商密钥。
  • 对称加密(AES/ChaCha20):快,用来加密真正的数据。
  • 一句话:用非对称的"安全"来传递对称的"钥匙"。

证书链:服务器证书 > 中间 CA > 根 CA(内置在浏览器/系统里)。浏览器逐级验证签名,直到信任的根。这就是为什么"自签名证书"会报警——没有可信的根为它背书。

为什么这么设计 ​

反事实 1:如果 HTTP 不加密 ​

  • 同一 WiFi 下的人能看到你所有的请求内容(明文)。
  • 中间人可以篡改响应(往网页里插广告、改支付账号)。
  • 无法确认"你连的真的是银行网站"(DNS 劫持 + 假冒服务器)。

反事实 2:如果只用非对称加密 ​

每个请求都要做一次 RSA 运算,比对称加密慢 2~3 个数量级。一个网页几十个请求,CPU 直接爆。

反事实 3:如果没有缓存 ​

每次打开网页都要重新下载所有资源。一个 2MB 的页面,弱网下要十几秒。缓存把"重复下载"变成"问一句变没变"(304 响应只有几百字节)。

面试怎么问 ​

Q:HTTP/1.1、HTTP/2、HTTP/3 的区别?

一条主线:都在解决"往返次数"和"队头阻塞"。 1.1 加持久连接(省握手)、加管道化(但响应必须按序,队头阻塞没解决)。 2.0 二进制分帧 + 多路复用(解决应用层队头阻塞)+ 头部压缩。 3.0 换 QUIC(UDP),解决 TCP 层队头阻塞 + 更快的建连 + 连接迁移。

Q:强缓存和协商缓存的区别?

强缓存不发请求,直接看本地是否过期(Cache-Control: max-age)。 协商缓存要发请求,带 If-None-Match / If-Modified-Since,服务器判断没变就回 304(不带 body)。 顺序:先强缓存,过期了才走协商。

Q:HTTPS 的握手过程?为什么要非对称 + 对称?

非对称只用来协商密钥(安全但慢),对称用来传数据(快)。证书解决"我怎么确认公钥是你的"这个问题。

Q:为什么 HTTPS 能防中间人?

因为证书由 CA 签名,浏览器内置了可信根。中间人伪造证书需要 CA 的私钥,做不到;用自签证书浏览器会报警。 但要补一句:如果用户手动信任了恶意根证书(或系统被装后门),HTTPS 也防不住。

追问链:

HTTP 和 HTTPS 区别?
  > HTTPS 怎么加密?(TLS)
    > 用对称还是非对称?(混合:非对称协商密钥,对称传数据)
      > 为什么不全用非对称?(慢几百倍)
        > 怎么确认公钥是对方的?(证书 + CA 签名)
          > 证书链怎么验证?(逐级验签到内置根)
            > 那中间人能解密吗?(不能,没有私钥)

和其他知识的连接 ​

自测 3 问 ​

Q1:Cache-Control: no-cache 和 no-store 有什么区别?

点开看答案
  • no-cache:可以缓存,但每次使用前必须向服务器确认(走协商缓存)。名字有误导性,它禁止的不是"缓存",而是"直接用缓存"。
  • no-store:完全禁止缓存,不写磁盘、不写内存,每次都重新请求。

什么时候用:

  • 内容会变但想省流量 > no-cache(用 ETag 协商,没变就 304)。
  • 敏感数据(银行页面、token) > no-store(连本地副本都不留)。

这是最经典的 HTTP 缓存陷阱题。

Q2:为什么 HTTP/2 的多路复用不能解决 TCP 队头阻塞?

点开看答案

因为 HTTP/2 的多路复用是在 TCP 之上做的:多个流被切成帧,交错发在同一条 TCP 连接里。

而 TCP 是"按序交付的字节流",它不知道帧的边界。一旦某个 TCP 段丢了,后面的所有字节都要在内核缓冲区里等,包括其他流的帧。

所以:

  • HTTP/1.1 的队头阻塞在应用层(一个请求慢,后面排队)。
  • HTTP/2 解决了应用层的,但把问题下沉到了 TCP 层——而且更糟:所有流挤在一条连接上,一个丢包卡住全部。

HTTP/3 用 QUIC 在用户态实现可靠传输,每个流独立编号、独立重传,才真正做到"一个流丢包不影响其他流"。

Q3:为什么 502 和 504 要分开?

点开看答案

因为它们指向完全不同的故障,排查方向相反:

  • 502 Bad Gateway:网关(Nginx)连上了上游,但上游返回了非法响应(连接被重置、响应格式错、进程崩了)。> 去查上游进程是否存活、日志有没有崩溃。
  • 504 Gateway Timeout:网关连上了上游,也发了请求,但上游在超时时间内没回。> 去查上游为什么慢(慢 SQL、下游依赖、死锁)。

一个"坏了",一个"慢了",处理方式完全不同。这是线上排障的基本功。

类比失效处 / 易错点 ​

  • "一问一答"会让人以为 HTTP/1.1 不能并发。实际上浏览器会开 6 个左右的 TCP 连接并行请求。
  • HTTP 是无状态的,但 Cookie 让它"看起来有状态"。状态存在客户端,服务器每次都要重新识别——所以才有 Session、Token。
  • 301 会被浏览器永久缓存。发错了很难撤回(用户清了缓存才恢复),所以不确定时用 302/307。
  • HTTPS 不加密域名(SNI 明文)。中间人能看到你访问哪个网站,只是看不到内容。ECH(Encrypted Client Hello)在解决这个问题,但还没普及。
  • max-age 是相对时间,Expires 是绝对时间。Expires 依赖客户端时钟,时钟不准就失效,所以现代都用 max-age。
  • HTTP/2 的 Server Push 已经被主流放弃(Chrome 已移除),别在面试里当亮点讲。

✅ 验收清单 ​

  • 能写出一个完整的 HTTP 请求报文和响应报文(含请求行/状态行、头、空行、body)
  • 能说出 GET 和 POST 在"安全/幂等"上的区别,并解释为什么支付接口要幂等键
  • 能区分 401/403、502/504,并说出各自的排查方向
  • 能用一条主线讲清 HTTP/1.0 > 1.1 > 2 > 3 各自解决了什么问题
  • 能说清强缓存和协商缓存的顺序、各自的头字段、以及 ETag 为什么优于 Last-Modified
  • 能解释 no-cache 和 no-store 的区别(这是陷阱题)
  • 能讲清 TLS 握手的核心步骤,并解释"为什么非对称 + 对称混用"
  • 合上书,用自己的话讲 3 分钟,中途不卡壳