HTTP 协议
超文本传输协议(HyperText Transfer Protocol),是 Web 应用层最核心的通信协议,定义了客户端(浏览器)与服务器之间的请求/响应格式与交互规则。
发展历程
| 版本 | 时间 | 核心改进 |
|---|---|---|
| HTTP/0.9 | 1991 | 只有 GET 请求,无状态行,无头部,无状态码 |
| HTTP/1.0 | 1996 | 引入版本号、状态码、头部字段,支持多种文件类型 |
| HTTP/1.1 | 1997 | 持久连接(keep-alive)、管道化、分块传输编码、Host 头部 |
| HTTP/2 | 2015 | 二进制分帧、多路复用、头部压缩(HPACK)、服务器推送 |
| HTTP/3 | 2022 | 基于 QUIC(UDP),0-RTT 连接、改进的拥塞控制 |
HTTP/1.1 核心特性
请求-响应模型
客户端 服务器
│ │
├────── 请求(Request)───────→│
│ │
│←────── 响应(Response)──────┤
│ │请求结构
GET /api/users HTTP/1.1 ← 请求行(方法 + URL + 版本)
Host: example.com ← 请求头
User-Agent: Mozilla/5.0
Accept: application/json
Authorization: Bearer xxxxxx
← 空行分隔
{ "id": 1 } ← 请求体(可选,GET 无)响应结构
HTTP/1.1 200 OK ← 状态行(版本 + 状态码 + 原因短语)
Content-Type: application/json
Content-Length: 45
Cache-Control: max-age=3600
← 空行分隔
{ "name": "Alice", "age": 30 } ← 响应体请求方法
| 方法 | 含义 | 幂等 | 安全 | 有无请求体 |
|---|---|---|---|---|
| GET | 获取资源 | 是 | 是 | 无 |
| HEAD | 获取响应头(类似 GET 但无响应体) | 是 | 是 | 无 |
| POST | 提交/创建资源 | 否 | 否 | 有 |
| PUT | 全量更新资源 | 是 | 否 | 有 |
| PATCH | 部分更新资源 | 否 | 否 | 有 |
| DELETE | 删除资源 | 是 | 否 | 可有可无 |
| OPTIONS | 获取服务器支持的请求方法 | 是 | 是 | 无 |
| TRACE | 回显请求,用于诊断 | 是 | 是 | 无 |
幂等:多次执行与一次执行结果相同。 安全:不会修改服务器资源。
状态码
1xx 信息
| 状态码 | 含义 | 说明 |
|---|---|---|
| 100 | Continue | 客户端应继续发送请求体 |
| 101 | Switching Protocols | 切换到 WebSocket 等协议 |
| 102 | Processing | 服务器已收到但正在处理(WebDAV) |
2xx 成功
| 状态码 | 含义 | 说明 |
|---|---|---|
| 200 | OK | 请求成功 |
| 201 | Created | 创建成功(POST/PUT) |
| 202 | Accepted | 已接受但尚未完成处理 |
| 204 | No Content | 成功无响应体(DELETE) |
| 206 | Partial Content | 返回部分内容(断点续传) |
3xx 重定向
| 状态码 | 含义 | 说明 |
|---|---|---|
| 301 | Moved Permanently | 永久重定向(GET 请求保持方法) |
| 302 | Found | 临时重定向(可能改变请求方法) |
| 303 | See Other | 用 GET 访问另一个 URI |
| 304 | Not Modified | 资源未修改(配合缓存) |
| 307 | Temporary Redirect | 临时重定向(保持原请求方法) |
| 308 | Permanent Redirect | 永久重定向(保持原请求方法) |
4xx 客户端错误
| 状态码 | 含义 | 说明 |
|---|---|---|
| 400 | Bad Request | 请求格式错误 |
| 401 | Unauthorized | 未认证 |
| 403 | Forbidden | 无权限访问 |
| 404 | Not Found | 资源不存在 |
| 405 | Method Not Allowed | 不允许的方法 |
| 408 | Request Timeout | 请求超时 |
| 409 | Conflict | 资源冲突 |
| 410 | Gone | 资源已永久删除 |
| 413 | Payload Too Large | 请求体太大 |
| 429 | Too Many Requests | 请求频率过高(限流) |
| 431 | Request Header Fields Too Large | 请求头太大 |
5xx 服务端错误
| 状态码 | 含义 | 说明 |
|---|---|---|
| 500 | Internal Server Error | 服务器内部错误 |
| 501 | Not Implemented | 不支持的功能 |
| 502 | Bad Gateway | 网关错误 |
| 503 | Service Unavailable | 服务不可用(过载/维护) |
| 504 | Gateway Timeout | 网关超时 |
| 505 | HTTP Version Not Supported | 不支持的 HTTP 版本 |
常见请求头
| 请求头 | 说明 | 示例 |
|---|---|---|
Host | 必需,目标主机 | Host: api.example.com |
User-Agent | 客户端标识 | Mozilla/5.0 ... |
Accept | 可接受的响应类型 | application/json |
Accept-Encoding | 可接受的压缩方式 | gzip, deflate, br |
Accept-Language | 可接受的语言 | zh-CN,zh;q=0.9 |
Authorization | 认证凭证 | Bearer token123 |
Content-Type | 请求体类型 | application/json |
Content-Length | 请求体字节数 | 45 |
Cookie | Cookie 数据 | sessionId=abc123 |
Referer | 来源页面 URL | https://example.com/page |
Origin | 请求来源(CORS) | https://example.com |
Cache-Control | 缓存策略 | no-cache, max-age=3600 |
If-Modified-Since | 条件请求(缓存) | Mon, 23 May 2025... |
If-None-Match | 条件请求(ETag) | "abc123" |
常见响应头
| 响应头 | 说明 | 示例 |
|---|---|---|
Content-Type | 响应体类型 | application/json; charset=utf-8 |
Content-Length | 响应体字节数 | 45 |
Content-Encoding | 压缩方式 | gzip |
Set-Cookie | 设置 Cookie | sessionId=abc123; Path=/ |
Cache-Control | 缓存策略 | public, max-age=3600 |
ETag | 资源标识(用于缓存验证) | "abc123" |
Last-Modified | 最后修改时间 | Mon, 23 May 2025... |
Location | 重定向目标 URL | https://example.com/new |
Access-Control-Allow-Origin | CORS 允许的源 | * |
WWW-Authenticate | 认证方式 | Bearer realm="example" |
HTTP/2 核心改进
二进制分帧
HTTP/1.1 是文本协议,HTTP/2 改为二进制协议,将消息拆分为更细粒度的帧。
HTTP/1.1(文本):
GET /index.html HTTP/1.1
Host: example.com
HTTP/2(二进制帧):
HEADERS frame (Stream 1): { :method: GET, :path: /index.html, :authority: example.com }帧类型:
| 帧类型 | 说明 |
|---|---|
HEADERS | 请求/响应头 |
DATA | 请求/响应体 |
SETTINGS | 连接参数协商 |
PRIORITY | 流优先级 |
RST_STREAM | 终止流 |
GOAWAY | 关闭连接 |
PING | 心跳检测 |
多路复用
HTTP/1.1 的队头阻塞问题:一个连接上只能串行处理请求。
HTTP/1.1(串行):
请求1 → 等待 → 响应1 → 请求2 → 等待 → 响应2
HTTP/2(并行):
请求1 ──→ 响应1 (Stream 1)
请求2 ──→ 响应2 (Stream 3) ← 在同一个 TCP 连接上并行
请求3 ──→ 响应3 (Stream 5)头部压缩(HPACK)
HTTP/1.1 每次请求都携带大量重复头部(如 Cookie、User-Agent)。
HTTP/1.1 头部大小:约 800 字节
HTTP/2 HPACK 压缩后:约 30 字节(首次)→ 约 8 字节(后续)压缩方式:
- 静态表 — 预定义的 61 个常用头部(如
:method: GET) - 动态表 — 通信过程中动态建立
- Huffman 编码
服务器推送
服务器可以主动向客户端推送资源,无需客户端请求。
客户端 服务器
│ │
├── GET /index.html ──────→ │
│ ├── Push /style.css ← 主动推送
│ ├── Push /app.js ← 主动推送
│←── 200 /index.html ──────┤
│←── /style.css ───────────┤
│←── /app.js ──────────────┤HTTP/3 与 QUIC
基于 QUIC(Quick UDP Internet Connections)协议,而非 TCP。
| 特性 | HTTP/2 over TCP | HTTP/3 over QUIC |
|---|---|---|
| 传输层 | TCP | UDP + QUIC |
| 连接建立 | 3 次 TCP 握手 + TLS 1.3 | 0-RTT / 1-RTT |
| 队头阻塞 | TCP 层面仍有队头阻塞 | 无(流独立) |
| 连接迁移 | 不支持(IP 变化需重连) | 支持(连接 ID 标识) |
| 加密 | 可选(TLS) | 强制内置 |
TCP 队头阻塞问题:
丢失一个包时,整个 TCP 连接上的所有流都需等待重传:
TCP 流: [包1][包2][包3] ← 包2 丢失
↕ 等待重传 ↕
[包1][包2][包3] ← 包1、包3 虽然收到了,但无法交付给上层
QUIC 流独立:
Stream 1: [包A1][包A2][包A3] ← 包A2 丢失,只影响 Stream 1
Stream 2: [包B1][包B2][包B3] ← Stream 2 不受影响,继续处理HTTPS
HTTPS = HTTP + TLS/SSL,通过在 HTTP 和 TCP 之间加入 TLS 层实现加密通信。
HTTP: │ HTTP │ TCP │ IP │
HTTPS: │ HTTP │ TLS │ TCP │ IP │TLS 握手过程(简化)
客户端 服务器
│ │
├── ClientHello →│ 支持的 TLS 版本、密码套件、随机数
│← ServerHello │ 选定的版本、密码套件、服务器随机数
│← Certificate │ 服务器证书(含公钥)
│← ServerHelloDone │
│ │
├── ClientKeyExchange →│ 用服务器公钥加密的预主密钥
├── ChangeCipherSpec →│ 后续通信加密
├── Finished →│
│ │
│← ChangeCipherSpec │
│← Finished │
│ │
├─────── 加密通信开始 ──────────→ │常见密码套件
TLS_AES_128_GCM_SHA256
│ │ │
│ │ └── 密钥交换与身份认证算法
│ └── 对称加密算法与模式
└── 密钥交换算法Cookie
HTTP 是无状态的协议,Cookie 用于在客户端存储状态信息。
工作流程
客户端 服务器
│ │
│── GET /login ──────────────────→│
│← Set-Cookie: sessionId=abc123 ──┤
│ │
│── GET /profile ────────────────→│ ← 自动携带 Cookie
│ Cookie: sessionId=abc123 │
│← 200 /profile ──────────────────┤Cookie 属性
| 属性 | 说明 |
|---|---|
Domain | 允许发送 Cookie 的域名 |
Path | 允许发送 Cookie 的路径 |
Max-Age | 过期时间(秒) |
Secure | 仅通过 HTTPS 发送 |
HttpOnly | 禁止 JavaScript 访问 |
SameSite | 限制跨站请求发送策略(Strict / Lax / None) |
SameSite 策略
| 值 | 说明 |
|---|---|
Strict | 所有跨站请求都不发送 |
Lax | 默认值,链接和 GET 提交可发送 |
None | 所有跨站请求都发送(必须配合 Secure) |
缓存
强缓存
不向服务器发送请求,直接从本地缓存读取。
客户端 服务器
│ │
├──── /logo.png ────→│
│← Cache-Control: │
│ max-age=3600 │
│ │
├──── /logo.png (×) │ ← 缓存有效期内,不请求服务器
│ 从本地缓存读取 │| 响应头 | 说明 |
|---|---|
Cache-Control: max-age=3600 | 缓存 3600 秒 |
Cache-Control: no-cache | 协商缓存(必须验证) |
Cache-Control: no-store | 禁止缓存 |
Expires: Mon, 23 May 2026... | 过期时间(HTTP/1.0,优先级低于 Cache-Control) |
协商缓存
向服务器验证资源是否变更,未变更则返回 304。
客户端 服务器
│ │
├── /style.css ──────→│
│← 200 + ETag: "v2" │
│ + Last-Modified:...│
│ │
├── /style.css ──────→│
│ If-None-Match: │
│ "v2" │
│← 304 Not Modified │ ← 资源未变,使用缓存| 请求头 | 响应头 | 说明 |
|---|---|---|
If-None-Match: "xxx" | ETag: "xxx" | 基于内容哈希的强验证 |
If-Modified-Since: date | Last-Modified: date | 基于时间的弱验证 |
CORS(跨域资源共享)
浏览器同源策略限制不同源之间的请求。CORS 通过 HTTP 头来允许跨域访问。
简单请求
满足以下条件:
- 方法:GET、HEAD、POST
- 请求头:仅包含
Accept、Accept-Language、Content-Language、Content-Type(限定值)
浏览器直接发送请求,通过 Origin 头判断。
客户端(http://a.com) 服务器
│ │
├── GET /api/data ─────────────────→│
│ Origin: http://a.com │
│← Access-Control-Allow-Origin: * │预检请求
对于非简单请求,浏览器先发送 OPTIONS 预检请求:
客户端 服务器
│ │
├── OPTIONS /api/upload ─────────────────────→ │
│ Origin: http://a.com │
│ Access-Control-Request-Method: PUT │
│ Access-Control-Request-Headers: X-Custom │
│ │
│← Access-Control-Allow-Origin: http://a.com │
│ Access-Control-Allow-Methods: GET, POST, PUT│
│ Access-Control-Allow-Headers: X-Custom │
│ Access-Control-Max-Age: 86400 │
│ │
├── PUT /api/upload ──────────────────────────→ │CORS 常见响应头
| 响应头 | 说明 |
|---|---|
Access-Control-Allow-Origin | 允许的来源(* 或具体域名) |
Access-Control-Allow-Methods | 允许的方法 |
Access-Control-Allow-Headers | 允许的请求头 |
Access-Control-Allow-Credentials | 是否允许携带凭据 |
Access-Control-Max-Age | 预检请求缓存时间(秒) |
Access-Control-Expose-Headers | 允许 JS 访问的响应头 |
内容协商
客户端和服务器就响应内容的格式达成一致的过程。
基于请求头的协商
| 请求头 | 响应头 | 说明 |
|---|---|---|
Accept | Content-Type | 媒体类型,如 application/json |
Accept-Language | Content-Language | 语言,如 zh-CN |
Accept-Encoding | Content-Encoding | 压缩方式,如 gzip |
基于 URL 的协商
/api/users.json → JSON 格式
/api/users.xml → XML 格式
/api/users → 由 Accept 头决定连接管理
HTTP/1.1 持久连接
HTTP/1.0:每个请求新建 TCP 连接
┌─ TCP 握手 ─┬─ 请求/响应 ─┬─ TCP 挥手 ─┐
┌─ TCP 握手 ─┬─ 请求/响应 ─┬─ TCP 挥手 ─┐
HTTP/1.1:复用 TCP 连接
┌─ TCP 握手 ─┬─ 请求/响应1 ─┬─ 请求/响应2 ─┬─ ... ─┬─ TCP 挥手 ─┐http
# 响应头
Connection: keep-alive
Keep-Alive: timeout=5, max=1000管道化(Pipeline)
HTTP/1.1 的管道化允许连续发送多个请求而无需等待响应,但实际中未被广泛采用。
请求1 ──→ 请求2 ──→ 请求3 ──→
┌─ 响应1
┌─ 响应2 ← 必须按顺序返回
┌─ 响应3常见 Content-Type
| Content-Type | 说明 |
|---|---|
text/html | HTML 文档 |
text/plain | 纯文本 |
text/css | CSS 样式表 |
application/javascript | JavaScript |
application/json | JSON 数据 |
application/xml | XML 数据 |
application/x-www-form-urlencoded | 表单提交(URL 编码) |
multipart/form-data | 文件上传 |
image/png | PNG 图片 |
image/jpeg | JPEG 图片 |
application/octet-stream | 二进制流(文件下载) |
application/pdf | PDF 文档 |
常用工具
curl
bash
# GET 请求
curl https://api.example.com/users
# POST JSON
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice","age":30}'
# 查看响应头
curl -I https://example.com
# 携带 Cookie
curl -b "sessionId=abc123" https://example.com/profile
# 跟随重定向
curl -L https://tinyurl.com/xxx
# 详细输出
curl -v https://example.com浏览器开发者工具
F12 → Network 面板是调试 HTTP 请求最常用的工具:
- 查看请求/响应头、请求体、响应体
- 查看请求时间线(DNS、TCP、TLS、TTFB、下载)
- 过滤请求类型(XHR、JS、CSS、IMG)
- 模拟限速、离线状态
- 复制为 cURL 命令