> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# HTTP 协议深度指南
- URL: https://blog.vercanti.com/http-xie-yi-shen-du-zhi-nan/
- Published: 2026-08-28T14:35:46.000Z
- Updated: 2026-08-28T14:59:35.000Z
- Description: HTTP（HyperText Transfer Protocol）是 Web 的基础传输协议，基于 TCP/IP，采用请求/响应模型。 相关文档：Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性：多次执行相同请求，服务器状态结果相同。PUT /users/1 多次执行结果一致；POST /users 每次创建新资源，非幂等。 浏览器直接从本地缓存读取，不向服务器发送请求。 缓存命中时，状
- Author: yellowdog
- Tags: 网络协议

> 官方文档：<https://developer.mozilla.org/zh-CN/docs/Web/HTTP>  
> 适用版本：HTTP/1.1（RFC 7231）/ HTTP/2（RFC 7540）/ HTTP/3（RFC 9114）（2026-05-07 核实）

HTTP（HyperText Transfer Protocol）是 Web 的基础传输协议，基于 TCP/IP，采用请求/响应模型。

相关文档：[Web安全基础](https://blog.vercanti.com/web-an-quan-ji-chu/) [FastAPI完全指南](https://blog.vercanti.com/fastapi-wan-quan-zhi-nan/) [Nginx完全指南](https://blog.vercanti.com/nginx-wan-quan-zhi-nan/)

---

## HTTP/1.1 核心

### 请求报文结构

```
起始行（Request Line）
  方法 路径 协议版本
  GET /api/users?page=1 HTTP/1.1

Headers（请求头，每行一个键值对，以空行结束）
  Host: api.example.com
  User-Agent: Mozilla/5.0
  Accept: application/json

Body（请求体，GET/HEAD 通常无 Body）
  {"username": "alice"}

```

### 常用请求头

| 请求头               | 说明                                              |
| ----------------- | ----------------------------------------------- |
| Host              | 目标服务器域名和端口（HTTP/1.1 必须携带），用于虚拟主机路由              |
| User-Agent        | 客户端标识，包含浏览器/OS 版本信息                             |
| Accept            | 客户端接受的响应内容类型，如 application/json, text/html      |
| Accept-Encoding   | 客户端支持的压缩方式，如 gzip, deflate, br                  |
| Accept-Language   | 首选语言，如 zh-CN,zh;q=0.9                           |
| Content-Type      | 请求体的媒体类型，如 application/json、multipart/form-data |
| Content-Length    | 请求体字节长度                                         |
| Authorization     | 认证凭据，如 Bearer <token> 或 Basic <base64>          |
| Cookie            | 发送给服务器的 Cookie 键值对                              |
| Cache-Control     | 请求端缓存指令，如 no-cache（强制向服务器验证）                    |
| If-Modified-Since | 协商缓存，携带上次响应的 Last-Modified 值                    |
| If-None-Match     | 协商缓存，携带上次响应的 ETag 值                             |
| Origin            | 跨域请求的来源，格式 scheme://host:port                   |
| Referer           | 当前请求的来源页面 URL                                   |
| Connection        | 连接控制，keep-alive 复用 TCP 连接，close 关闭              |

### 常用响应头

| 响应头                         | 说明                                            |
| --------------------------- | --------------------------------------------- |
| Content-Type                | 响应体的媒体类型，如 application/json; charset=utf-8    |
| Content-Length              | 响应体字节长度                                       |
| Content-Encoding            | 响应体的压缩方式，如 gzip                               |
| Transfer-Encoding           | 传输编码，chunked 表示分块传输（不含 Content-Length）        |
| Set-Cookie                  | 设置客户端 Cookie，可携带多个属性                          |
| Location                    | 重定向目标 URL，配合 3xx 状态码使用                        |
| ETag                        | 资源版本标识符（实体标签），用于协商缓存                          |
| Last-Modified               | 资源最后修改时间，用于协商缓存                               |
| Cache-Control               | 缓存策略指令，如 max-age=3600                         |
| Vary                        | 说明响应内容根据哪些请求头变化，影响缓存键，如 Vary: Accept-Encoding |
| Access-Control-Allow-Origin | CORS 响应，允许的请求来源                               |
| Strict-Transport-Security   | HSTS，强制使用 HTTPS                               |
| X-Content-Type-Options      | 防止 MIME 类型嗅探，值为 nosniff                       |

### 状态码分类

| 分类  | 范围      | 含义               | 常见状态码                                                                                                                                                                                                           |
| --- | ------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1xx | 100-199 | 信息性响应，请求已接收，继续处理 | 100 Continue：客户端可继续发送请求体                                                                                                                                                                                        |
| 2xx | 200-299 | 成功               | 200 OK：请求成功；201 Created：资源已创建；204 No Content：成功但无响应体；206 Partial Content：范围请求部分内容                                                                                                                               |
| 3xx | 300-399 | 重定向              | 301 Moved Permanently：永久重定向（浏览器缓存）；302 Found：临时重定向；304 Not Modified：协商缓存命中；307 Temporary Redirect：临时重定向，保持方法；308 Permanent Redirect：永久重定向，保持方法                                                                  |
| 4xx | 400-499 | 客户端错误            | 400 Bad Request：请求格式错误；401 Unauthorized：未认证；403 Forbidden：无权限；404 Not Found：资源不存在；405 Method Not Allowed：方法不允许；409 Conflict：资源冲突；422 Unprocessable Entity：语义错误（FastAPI 默认用于参数校验失败）；429 Too Many Requests：请求频率超限 |
| 5xx | 500-599 | 服务端错误            | 500 Internal Server Error：服务器内部错误；502 Bad Gateway：网关错误（上游服务不可用）；503 Service Unavailable：服务暂不可用；504 Gateway Timeout：网关超时                                                                                         |

---

## HTTP 方法

| 方法      | 语义             | 是否幂等  | 是否有请求体 | 典型用途              |
| ------- | -------------- | ----- | ------ | ----------------- |
| GET     | 获取资源           | 是     | 否      | 查询数据，不改变服务器状态     |
| POST    | 创建资源/提交数据      | 否     | 是      | 创建新资源，提交表单，RPC 调用 |
| PUT     | 完整替换资源         | 是     | 是      | 以请求体完整替换目标资源      |
| PATCH   | 部分更新资源         | 否（通常） | 是      | 仅更新资源的部分字段        |
| DELETE  | 删除资源           | 是     | 否（可选）  | 删除指定资源            |
| HEAD    | 获取响应头，不返回 Body | 是     | 否      | 检查资源是否存在、获取元数据    |
| OPTIONS | 查询支持的方法        | 是     | 否      | CORS 预检请求，查询跨域权限  |

幂等性：多次执行相同请求，服务器状态结果相同。`PUT /users/1` 多次执行结果一致；`POST /users` 每次创建新资源，非幂等。

---

## 缓存机制

### 强缓存

浏览器直接从本地缓存读取，不向服务器发送请求。

```
响应头：
Cache-Control: max-age=86400   # 缓存有效期 86400 秒（优先级高）
Expires: Thu, 01 Jan 2026 00:00:00 GMT  # 绝对过期时间（优先级低，已被 max-age 取代）

```

缓存命中时，状态码为 `200`（from memory cache / from disk cache）。

### 协商缓存

浏览器携带缓存标识向服务器验证，服务器判断资源是否变化。资源未变化时服务器返回 `304 Not Modified`，浏览器使用本地缓存；变化时返回 `200` 及新资源。

两种协商缓存机制：

```
# 机制一：ETag（精确，优先级高）
响应头：ETag: "abc123"
请求头：If-None-Match: "abc123"

# 机制二：Last-Modified（时间精度为秒，优先级低）
响应头：Last-Modified: Wed, 01 Jan 2025 00:00:00 GMT
请求头：If-Modified-Since: Wed, 01 Jan 2025 00:00:00 GMT

```

### Cache-Control 指令

| 指令              | 适用方   | 说明                                |
| --------------- | ----- | --------------------------------- |
| max-age=N       | 响应    | 资源缓存有效期（秒），从响应时间起算                |
| s-maxage=N      | 响应    | 共享缓存（CDN/代理）的有效期，覆盖 max-age       |
| no-cache        | 请求/响应 | 不直接使用缓存，每次必须向服务器验证（协商缓存仍可用）       |
| no-store        | 响应    | 禁止任何缓存存储，每次必须重新请求（比 no-cache 更严格） |
| public          | 响应    | 允许任何缓存（包括 CDN）存储                  |
| private         | 响应    | 仅允许浏览器私有缓存存储，不允许 CDN 缓存           |
| must-revalidate | 响应    | 缓存过期后必须向服务器验证，不允许使用过期缓存           |
| immutable       | 响应    | 资源内容永不变化，缓存有效期内不发送验证请求            |

---

## HTTPS / TLS

### TLS 握手流程（简化版，TLS 1.2）

```
Client                              Server
  |------ ClientHello ----------------->|  客户端发送：支持的 TLS 版本、密码套件、随机数(C)
  |<----- ServerHello ------------------|  服务器选择：TLS 版本、密码套件、随机数(S)
  |<----- Certificate ------------------|  服务器发送：数字证书（含公钥）
  |<----- ServerHelloDone --------------|
  |                                     |
  | （客户端验证证书合法性）              |
  |                                     |
  |------ ClientKeyExchange ----------->|  客户端发送：预主密钥（用服务器公钥加密）
  |------ ChangeCipherSpec ------------>|  通知：后续使用协商的加密算法
  |------ Finished -------------------->|  发送握手摘要（已加密）
  |<----- ChangeCipherSpec -------------|
  |<----- Finished ---------------------|
  |                                     |
  |<========= 加密的应用数据 ==========>|  双方用会话密钥加密通信

```

双方用随机数(C) + 随机数(S) + 预主密钥共同生成会话密钥（对称加密），后续通信使用此密钥。

### 证书链验证

服务器证书由中间 CA 签发，中间 CA 证书由根 CA 签发。浏览器内置信任的根 CA 证书列表，从叶证书逐级向上验证签名直到根 CA，全部验证通过则信任该证书。

### TLS 1.2 vs TLS 1.3

| 维度   | TLS 1.2    | TLS 1.3               |
| ---- | ---------- | --------------------- |
| 握手轮次 | 2-RTT      | 1-RTT（首次），0-RTT（会话恢复） |
| 密码套件 | 多种，包含已知弱算法 | 仅保留安全算法，废除 RSA 密钥交换   |
| 前向保密 | 可选         | 强制（ECDHE）             |
| 加密起始 | 握手最后阶段加密   | 握手第二个消息即开始加密          |
| 兼容性  | 极广         | 现代浏览器和服务器均已支持         |

### SNI（Server Name Indication）

TLS 握手的 ClientHello 中携带目标域名，允许同一 IP 的服务器为多个域名提供不同证书（类似 HTTP 中 `Host` 请求头的作用）。未携带 SNI 时，服务器只能返回默认证书。

---

## HTTP/2 与 HTTP/3

### HTTP/2 核心特性

- **多路复用（Multiplexing）**：单个 TCP 连接上并发传输多个请求/响应，帧（Frame）级别交错，解决了 HTTP/1.1 的队头阻塞（Head-of-Line Blocking）问题。
- **头部压缩（HPACK）**：使用静态表、动态表和 Huffman 编码压缩请求/响应头，减少重复头部的传输开销。
- **服务器推送（Server Push）**：服务器可以在客户端请求 HTML 之前主动推送 CSS/JS 资源，减少往返延迟。实际使用较少，已在 HTTP/3 中废弃。
- **二进制分帧**：HTTP/2 将报文分割为二进制帧（Frame），比 HTTP/1.1 的文本协议解析更高效。
- **流优先级**：客户端可声明请求的优先级，服务器按优先级分配资源。

### HTTP/3（QUIC）

HTTP/3 将传输层从 TCP 替换为 QUIC（基于 UDP）：

- **解决 TCP 层队头阻塞**：HTTP/2 的多路复用仍受 TCP 丢包影响，一个包丢失会阻塞所有流。QUIC 在传输层实现多路复用，流之间完全独立，单流丢包不影响其他流。
- **0-RTT 连接建立**：已知服务器时，QUIC 可以在握手的同时发送数据（0-RTT），比 TLS 1.2 over TCP 的 3-RTT 减少显著延迟。
- **连接迁移**：QUIC 使用连接 ID 标识连接（而非四元组），网络切换（如 Wi-Fi 切换到 4G）时连接不中断。

---

## Cookie

### Set-Cookie 属性

服务器通过 `Set-Cookie` 响应头设置 Cookie：

```
Set-Cookie: session_id=abc123; Domain=example.com; Path=/; Max-Age=3600; HttpOnly; Secure; SameSite=Lax

```

| 属性       | 说明                                                   |
| -------- | ---------------------------------------------------- |
| Domain   | Cookie 适用的域名，默认为当前域名（不含子域）。设置为 .example.com 则子域名共享   |
| Path     | Cookie 适用的路径，只有路径前缀匹配时才发送该 Cookie，默认为 /              |
| Expires  | 绝对过期时间（GMT 格式），未设置则为会话 Cookie，浏览器关闭即删除               |
| Max-Age  | 相对过期时间（秒），从接收时起算，优先级高于 Expires                       |
| HttpOnly | 禁止 JavaScript 通过 document.cookie 读取，防止 XSS 窃取 Cookie |
| Secure   | 仅在 HTTPS 连接中发送，HTTP 下不传输                             |
| SameSite | 跨站请求时的发送策略，见下方说明                                     |

### SameSite 三个值的区别

| 值      | 发送规则                                             | 适用场景                          |
| ------ | ------------------------------------------------ | ----------------------------- |
| Strict | 仅同站请求发送 Cookie，跨站完全不发送（包括从其他站点点链接跳转）             | 最严格，防 CSRF，但从外部链接进入时需重新登录     |
| Lax    | 跨站顶级导航（GET 请求）发送 Cookie，跨站的 POST/img/iframe 等不发送 | 兼顾安全与可用性，现代浏览器默认值             |
| None   | 跨站请求始终发送 Cookie，必须同时设置 Secure 属性                 | 第三方 Cookie 场景（如嵌入式 iframe 认证） |

### CSRF 攻击原理与防御

**攻击原理**：恶意网站诱导用户浏览时，利用浏览器自动携带目标站点 Cookie 的机制，伪造用户身份发起请求。例如用户登录银行后访问恶意网站，恶意网站自动向银行发送转账请求，浏览器携带银行 Cookie，银行无法区分请求来源。

**防御方案**：

1. `SameSite=Lax/Strict`：阻止跨站请求携带 Cookie，最简单有效。
2. CSRF Token：服务器在表单或响应中嵌入随机 Token，请求时必须携带，服务器验证 Token 合法性。恶意网站无法读取目标站点的 Token。
3. 验证 `Origin`/`Referer` 请求头：服务器检查请求来源，拒绝非预期来源的请求。
4. 双重 Cookie 验证：将 CSRF Token 同时存入 Cookie 和请求参数，服务器比对两者是否一致。

---

## 跨域（CORS）

同源策略（Same-Origin Policy）要求协议、域名、端口三者完全一致，否则浏览器阻止 JavaScript 读取响应。CORS（Cross-Origin Resource Sharing）是服务器声明允许跨域访问的机制。

### 简单请求 vs 预检请求

**简单请求**直接发送，满足以下全部条件：

- 方法为 `GET`、`HEAD`、`POST` 之一
- 请求头仅包含 `Accept`、`Accept-Language`、`Content-Language`、`Content-Type`（且值为 `text/plain`、`multipart/form-data`、`application/x-www-form-urlencoded`）

**预检请求（Preflight）**：不满足简单请求条件时，浏览器先发送 `OPTIONS` 请求询问服务器是否允许该跨域请求，服务器确认后再发送实际请求。触发场景：使用 `PUT`/`DELETE`/`PATCH`、携带 `Authorization` 头、`Content-Type` 为 `application/json` 等。

```
# 预检请求
OPTIONS /api/data HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type

# 预检响应
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

```

### CORS 响应头

| 响应头                              | 说明                                                         |
| -------------------------------- | ---------------------------------------------------------- |
| Access-Control-Allow-Origin      | 允许的来源，\* 表示所有，或指定具体 origin（携带 Cookie 时不能用 \*）              |
| Access-Control-Allow-Methods     | 预检响应，允许的 HTTP 方法列表                                         |
| Access-Control-Allow-Headers     | 预检响应，允许的请求头列表                                              |
| Access-Control-Expose-Headers    | 允许浏览器 JavaScript 读取的响应头（默认只能读取基本响应头）                       |
| Access-Control-Max-Age           | 预检结果的缓存时间（秒），缓存期内不再发送预检请求                                  |
| Access-Control-Allow-Credentials | 是否允许跨域请求携带 Cookie，值为 "true" 时客户端需设置 credentials: "include" |

### FastAPI CORS 中间件配置

```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.example.com", "https://admin.example.com"],
    allow_credentials=True,   # 允许 Cookie，此时 allow_origins 不能为 ["*"]
    allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
    allow_headers=["Authorization", "Content-Type", "X-Request-ID"],
    expose_headers=["X-Total-Count"],  # 允许前端 JS 读取的响应头
    max_age=86400,   # 预检缓存 24 小时
)

```

开发环境若需允许所有来源（不含 Cookie）：

```python
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=False,
    allow_methods=["*"],
    allow_headers=["*"],
)

```

---

## 踩坑与注意事项

**浏览器缓存导致请求未发出**：强缓存命中时（`Cache-Control: max-age` 未过期），浏览器直接使用缓存，Network 面板中请求显示为 "from memory cache" 或 "from disk cache"，完全不发送请求到服务器。调试时可在 DevTools 中勾选 "Disable cache" 或在请求中加 `Cache-Control: no-cache` 强制验证。API 接口通常应设置 `Cache-Control: no-store` 或 `Cache-Control: no-cache, must-revalidate`，避免动态数据被缓存。

**CORS 预检缓存（Access-Control-Max-Age）**：未设置 `Access-Control-Max-Age` 时，浏览器对每个跨域非简单请求都发送预检，增加一次额外的网络往返。应设置合理的缓存时间（如 `86400` 秒），减少预检请求数量。注意 Chrome 的上限为 7200 秒，Firefox 上限为 86400 秒。

**allow\_credentials 与 allow\_origins 的冲突**：FastAPI（及所有标准 CORS 实现）要求：当 `allow_credentials=True` 时，`allow_origins` 不能包含通配符 `"*"`，必须明确列出允许的 origin。否则浏览器会拒绝响应并报错。

**304 响应的处理**：协商缓存返回 `304` 时，响应体为空，浏览器使用本地缓存内容。服务器的 CORS 响应头（如 `Access-Control-Allow-Origin`）在 `304` 响应中也必须携带，否则浏览器会因 CORS 检查失败而报错，即使内容来自本地缓存。

---

## 最佳实践

**API 接口设置 `Cache-Control: no-store`**：动态数据（用户信息、订单列表）绝对不应被缓存。`no-store` 表示完全不缓存，每次必须请求服务器。`no-cache` 表示每次需要向服务器验证（发 If-None-Match），返回 304 时使用缓存。

**静态资源使用长期缓存 + 内容 hash 文件名**：JS/CSS 用 Vite/Webpack 生成 `main.[hash].js` 格式，`Cache-Control: max-age=31536000, immutable`；HTML 用 `no-store` 或短期缓存，保证新版本能及时下发。

**HTTPS 响应头加 HSTS**：防止降级攻击，告知浏览器未来一年内只用 HTTPS 访问本域名。

```http
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

```

**用 `Connection: keep-alive` 复用 TCP 连接**：HTTP/1.1 默认 keep-alive，但超时后需重建连接。HTTP/2 多路复用解决了 HTTP/1.1 的队头阻塞问题，生产环境推荐开启 HTTP/2。

**CORS 预检加 `Access-Control-Max-Age` 缓存**：非简单请求每次都发 OPTIONS 预检，设置 `Access-Control-Max-Age: 7200` 让浏览器缓存预检结果（Chrome 上限 7200 秒），减少额外请求。

---

## 常见陷阱

### 陷阱：浏览器强缓存导致请求未发出

**现象：** 修改了接口响应，但浏览器始终返回旧数据，DevTools Network 显示 "from disk cache"。

**原因：** 服务器响应头包含 `Cache-Control: max-age=3600`，未过期时浏览器完全不发请求。

**解决：** API 接口设置 `Cache-Control: no-store`；调试时在 DevTools 勾选 "Disable cache"（仅限 DevTools 打开时生效）。

---

### 陷阱：`allow_credentials=True` 时 CORS `allow_origins` 不能用 `*`

**现象：** 开启 Cookie 跨域后，浏览器报错：`The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'`。

**原因：** 当请求携带 credentials（Cookie / Authorization / TLS 客户端证书）时，浏览器要求 `Access-Control-Allow-Origin` 必须是具体的 origin，不允许通配符。

**解决：** 将 `allow_origins` 改为明确的 origin 列表，动态读取请求 `Origin` 头也可行。

```python
# FastAPI 示例
CORSMiddleware(app,
    allow_origins=["https://app.example.com"],  # 不能是 ["*"]
    allow_credentials=True,
)

```

---

### 陷阱：HTTP/2 推送与现代浏览器的兼容性

**现象：** 配置了 HTTP/2 Server Push 推送 CSS/JS，Chrome 120+ 后推送失效，资源还是单独加载。

**原因：** Chrome 从 106 开始移除了对 HTTP/2 Server Push 的支持（利用率极低且可能浪费带宽），改为推荐使用 `<link rel="preload">` 或 `103 Early Hints`。

**解决：** 用 `<link rel="preload" as="script" href="/app.js">` 替代 Server Push；或使用 `103 Early Hints` 响应提前告知资源（Nginx 1.25.1+ 支持）。

---

## 参见

- [Web安全基础](https://blog.vercanti.com/web-an-quan-ji-chu/)
- [FastAPI完全指南](https://blog.vercanti.com/fastapi-wan-quan-zhi-nan/)
- [Nginx完全指南](https://blog.vercanti.com/nginx-wan-quan-zhi-nan/)
- [WebSocket完全指南](https://blog.vercanti.com/websocket-wan-quan-zhi-nan/)