HTTP 协议深度指南

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 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

分享

官方文档: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安全基础 FastAPI完全指南 Nginx完全指南


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/jsonmultipart/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)时连接不中断。

服务器通过 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 预检请求

简单请求直接发送,满足以下全部条件:

  • 方法为 GETHEADPOST 之一
  • 请求头仅包含 AcceptAccept-LanguageContent-LanguageContent-Type(且值为 text/plainmultipart/form-dataapplication/x-www-form-urlencoded

预检请求(Preflight):不满足简单请求条件时,浏览器先发送 OPTIONS 请求询问服务器是否允许该跨域请求,服务器确认后再发送实际请求。触发场景:使用 PUT/DELETE/PATCH、携带 Authorization 头、Content-Typeapplication/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 中间件配置

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):

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-storeCache-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 访问本域名。

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 头也可行。

# 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 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog

数据结构完全参考

Python list 是动态数组(Dynamic Array): 推论:需要频繁头部操作时,用 collections.deque 替代 list。 循环链表将尾节点的 next 指向头节点。常见于约瑟夫环问题。使用时注意遍历终止条件应以初始节点为准,否则会无限循环。 栈遵循 LIFO(后进先出)原则。 队列遵循 FIFO(先进先出)原则。 Python heapq 是最小堆。实现最大堆时将值取反。 单调栈维护一个单调递增或递减的栈,用于求"下一个更大/更小元素"。 单调队列用于滑动窗口最值问题,队列中元素单调递减(求最大值)或单调递增(求最小值)。

By yellowdog