整理 HTTP 协议的核心知识点——状态码、常用 headers、Restful API 设计和两步缓存策略(强制缓存 + 协商缓存)。前后端协作中的大部分问题,都绕不开这些基础。
常见状态码
| 分类 | 含义 | 代表 |
|---|---|---|
| 1xx | 信息,服务器收到请求 | 101 Switching Protocols(WebSocket 握手) |
| 2xx | 成功 | 200 OK、201 Created、204 No Content |
| 3xx | 重定向 | 301、302、304 |
| 4xx | 客户端错误 | 400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found、405 Method Not Allowed、429 Too Many Requests |
| 5xx | 服务端错误 | 500 Internal Server Error、502 Bad Gateway、503 Service Unavailable、504 Gateway Timeout |
关键状态码的区别:
| 状态码 | 含义 | 行为 |
|---|---|---|
301 永久重定向 | 资源永久移动到新地址 | 浏览器记住新地址,下次直接跳转。换域名时使用 |
302 临时重定向 | 资源临时移动 | 每次仍访问原地址。短链接、未登录跳转常用 |
304 Not Modified | 资源未修改 | 使用缓存,不返回资源体 |
401 Unauthorized | 未认证 | 需要登录凭证 |
403 Forbidden | 无权限 | 已认证但权限不足 |
502 Bad Gateway | 网关错误 | 上游服务器返回无效响应 |
504 Gateway Timeout | 网关超时 | 上游服务器响应超时 |
常见 Headers
Request Headers
| Header | 含义 |
|---|---|
Accept | 可接收的响应格式(application/json、text/html) |
Accept-Encoding | 可接收的压缩算法(gzip、deflate、br) |
Accept-Language | 可接收的语言(zh-CN、en) |
Authorization | 认证信息(Bearer <token>) |
Cache-Control | 缓存指令(no-cache、max-age=0) |
Connection | keep-alive(复用 TCP 连接) |
Content-Type | 请求体格式(application/json、multipart/form-data) |
Cookie | 携带的 cookie |
Host | 请求的域名(HTTP/1.1 必需) |
Origin | 请求来源(用于 CORS) |
Referer | 来源页面 URL |
User-Agent | 客户端信息 |
Response Headers
| Header | 含义 |
|---|---|
Content-Type | 响应体格式和编码(application/json; charset=utf-8) |
Content-Length | 响应体大小(字节) |
Content-Encoding | 压缩算法(gzip、br) |
Set-Cookie | 设置 cookie |
Cache-Control | 缓存策略 |
ETag | 资源标识(协商缓存) |
Last-Modified | 资源最后修改时间 |
Access-Control-Allow-Origin | CORS 允许的域 |
Location | 重定向目标 URL(配合 3xx 状态码) |
缓存相关 Headers
| 强制缓存 | 协商缓存 |
|---|---|
Cache-Control | Last-Modified / If-Modified-Since |
Expires(已过时,被 Cache-Control 取代) | ETag / If-None-Match |
Restful API
一种 API 设计风格:把每个 URL 当作一个资源,用 HTTP method 表示操作类型。
| 操作 | 传统 | Restful |
|---|---|---|
| 获取单个 | GET /api/get-blog?id=100 | GET /api/blog/100 |
| 列表 | GET /api/list?page=2 | GET /api/blog?page=2 |
| 创建 | POST /api/create-blog | POST /api/blog |
| 更新 | POST /api/update-blog?id=100 | PUT /api/blog/100 或 PATCH /api/blog/100 |
| 删除 | POST /api/delete-blog?id=100 | DELETE /api/blog/100 |
PUT和PATCH的区别:PUT是全量替换,PATCH是部分更新。实际项目中PATCH的使用频率更高。
核心思想:URL 表示资源,method 表示操作。
HTTP 缓存策略
为什么需要缓存
网络请求是页面加载环节中最慢的部分。缓存的目的是:
- 减少请求数量和请求体积
- 提升加载速度(缓存命中时延迟接近 0)
- 降低服务器压力
- 减少网络不稳定带来的影响
哪些资源可以缓存?静态资源(JS、CSS、图片、字体)可以积极缓存;HTML 文档通常不做强缓存或缓存时间很短。
强制缓存
通过 Cache-Control 响应头控制,缓存有效期内浏览器不发起请求,直接用本地缓存。
Cache-Control: max-age=31536000 (秒,这里表示一年)
Cache-Control: public, max-age=86400, immutable
| 值 | 含义 |
|---|---|
max-age=N | 缓存 N 秒,从请求时间开始计算 |
no-cache | 可以缓存,但每次使用前必须向服务端验证(走协商缓存) |
no-store | 完全不缓存 |
private | 仅浏览器缓存,中间代理(CDN)不缓存 |
public | 允许中间代理缓存 |
must-revalidate | 过期后必须重新验证 |
immutable | 资源内容不变(配合 max-age 及 hash 文件名使用) |
Cache-Control 完全可以替代已过时的 Expires。两者同时出现时,Cache-Control 优先。
协商缓存
强制缓存过期后,浏览器带着资源标识(If-Modified-Since 或 If-None-Match)询问服务端”资源有没有变化”。没变返回 304,变了返回 200 + 新资源。
方案一:Last-Modified / If-Modified-Since
初次请求响应: Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
再次请求携带: If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
资源未变 → 304 Not Modified(不返回 body)
资源已变 → 200 + 新资源 + 新 Last-Modified
方案二:ETag / If-None-Match(优先使用)
初次请求响应: ETag: "abc123"
再次请求携带: If-None-Match: "abc123"
资源未变 → 304 Not Modified
资源已变 → 200 + 新资源 + 新 ETag
ETag 优于 Last-Modified 的原因:
- Last-Modified 只能精确到秒。一秒内多次修改,Last-Modified 不变
- 资源被重新生成但内容不变时,ETag 不变而 Last-Modified 会变
- 有些服务器无法精确获取文件的最后修改时间
两者同时存在时,浏览器会同时发送
If-None-Match和If-Modified-Since,服务器优先校验ETag。
完整的缓存决策流程
浏览器请求资源
↓
有缓存且 Cache-Control 未过期?
├── 是 → 直接用缓存(200 from disk/memory cache)
└── 否 → 发起请求,携带协商缓存标识
↓
服务端判断资源是否变化?
├── 未变 → 304,浏览器用缓存
└── 已变 → 200,返回新资源 + 新缓存标识
三种刷新操作对缓存的影响
| 操作 | 强制缓存 | 协商缓存 |
|---|---|---|
| 地址栏输入 URL、点击链接、前进后退 | ✅ 有效 | ✅ 有效 |
| F5 / 点击刷新按钮 / 右键菜单刷新 | ❌ 失效 | ✅ 有效 |
| Ctrl+F5 / Shift+刷新 | ❌ 失效 | ❌ 失效 |
F5 刷新时浏览器会在请求头添加
Cache-Control: max-age=0,跳过强制缓存。Ctrl+F5 则添加Cache-Control: no-cache和Pragma: no-cache,同时跳过强制缓存和协商缓存。