HTTP 详解:一次请求背后发生了什么?

在 Web 开发中,HTTP 几乎无处不在。

浏览器访问一个页面,前端调用一个 API,PHP、Java、Go 服务之间进行 RPC,下载文件,提交表单,这些场景背后都可能使用 HTTP。

但很多开发者真正理解的 HTTP 只是:

GET /users
POST /login
200 OK
Content-Type: application/json

如果继续往下追问:

浏览器输入一个 URL 后,到底发生了什么?

就会涉及 DNS、TCP、TLS、HTTP、连接复用、请求报文、响应报文、缓存等多个环节。

理解这些内容之后,很多常见问题都会变得容易:

  • 为什么第一次请求比较慢?

  • 为什么 HTTP/1.1 需要 Keep-Alive?

  • HTTP/2 为什么能够同时处理多个请求?

  • Cookie 和 Session 到底是什么关系?

  • 304 是什么意思?

  • 为什么 API 经常返回 401、403、404、409?

  • HTTPS 到底保护了什么?

  • HTTP 和 Socket、TCP 又是什么关系?

本文从一次实际请求开始,把这些概念串起来。


1. HTTP 到底是什么?

HTTP(Hypertext Transfer Protocol,超文本传输协议)是一个应用层的请求/响应协议。

它的核心模型非常简单:

Client
   │
   │ HTTP Request
   ▼
Server
   │
   │ HTTP Response
   ▼
Client

客户端发送 Request,服务端返回 Response。

例如:

GET /users/1001 HTTP/1.1
Host: example.com
Accept: application/json

服务器可能返回:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 35

{"id":1001,"name":"Tom","age":18}

HTTP 本身并不负责:

  • DNS 解析

  • TCP 连接建立

  • IP 路由

  • 数据包传输

  • TLS 加密

这些属于 HTTP 下面的网络层、传输层以及安全协议。

可以简单理解为:

应用
│
│ HTTP
▼
TCP / QUIC
│
│ IP
▼
网络

HTTP 规范目前将语义与不同版本的报文表达方式分开定义。核心语义由 RFC 9110 定义,HTTP/1.1、HTTP/2 分别由 RFC 9112 和 RFC 9113 定义。


2. 输入一个 URL 后发生了什么?

假设浏览器访问:

https://example.com/users/1001

简化之后,可以理解为:

浏览器
  │
  ├─ 1. DNS:example.com → IP
  │
  ├─ 2. 建立 TCP 连接
  │
  ├─ 3. TLS 握手
  │
  ├─ 4. 发送 HTTP 请求
  │
  ├─ 5. 服务端处理请求
  │
  ├─ 6. 返回 HTTP 响应
  │
  └─ 7. 浏览器解析响应

如果使用 HTTP/1.1,典型情况下可以理解为:

DNS
 ↓
TCP
 ↓
TLS
 ↓
HTTP

如果是 HTTP/3,则底层传输机制不同,它使用 QUIC,而不是 TCP。

因此不要把:

HTTPS = HTTP + TCP

当成严格定义。

更准确地说:

HTTP
 ├── HTTP/1.1 → 通常基于 TCP
 ├── HTTP/2   → 基于 TCP
 └── HTTP/3   → 基于 QUIC/UDP

HTTP/3 的核心变化并不是重新定义 HTTP 语义,而是改变底层传输方式。


3. HTTP 请求和响应是什么?

HTTP 最核心的两个东西就是:

Request
Response

3.1 HTTP 请求

一个典型的 HTTP/1.1 请求:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer xxx
Content-Length: 28

{"name":"Tom","age":18}

可以拆成:

请求方法
    ↓
POST

请求目标
    ↓
/api/users

协议版本
    ↓
HTTP/1.1

请求头
    ↓
Host
Content-Type
Authorization
Content-Length

请求内容
    ↓
JSON Body

其中:

Method

表示客户端希望对目标资源执行什么语义。

常见方法:

Method常见用途
GET获取资源
POST创建资源或执行操作
PUT整体更新资源
PATCH部分更新资源
DELETE删除资源
HEAD获取与 GET 类似的元信息,但不返回内容
OPTIONS获取通信选项

例如:

GET /users/1001

表示获取用户。

而:

DELETE /users/1001

表示删除用户。

需要注意,HTTP Method 不只是字符串名称,它本身具有协议定义的语义,例如安全性(safe)和幂等性(idempotent)等。


3.2 URI

例如:

https://example.com/users/1001?page=2

可以拆成:

scheme
 ↓
https

host
 ↓
example.com

path
 ↓
/users/1001

query
 ↓
?page=2

HTTP 使用 URI 来标识请求目标资源,而 Method 决定对资源执行什么操作。


3.3 Header

Header 用于传递各种元数据。

例如:

Content-Type: application/json
Accept: application/json
Authorization: Bearer xxx
User-Agent: Mozilla/5.0
Cache-Control: no-cache

常见 Header 可以简单分成几类:

请求相关
├── Host
├── Authorization
├── User-Agent
├── Accept
└── Cookie

内容相关
├── Content-Type
├── Content-Length
└── Content-Encoding

缓存相关
├── Cache-Control
├── If-None-Match
└── If-Modified-Since

响应相关
├── Location
├── Set-Cookie
├── ETag
└── Vary

Header 是 HTTP 可扩展性的一个重要组成部分。


3.4 Body

Body 用于承载请求或响应的数据。

例如:

{
  "username": "tom",
  "password": "123456"
}

Body 本身并没有固定格式。

可以是:

JSON
Form
XML
HTML
图片
文件
二进制数据

具体如何解释,通常通过:

Content-Type

告诉对方。

例如:

Content-Type: application/json

表示:

Body 是 JSON

4. HTTP 响应和状态码

服务端处理请求之后,会返回 Response:

HTTP/1.1 200 OK
Content-Type: application/json

{"id":1001,"name":"Tom"}

响应最重要的是:

状态码
Header
Body

4.1 2xx:成功

常见:

200 OK
201 Created
202 Accepted
204 No Content

例如创建用户:

HTTP/1.1 201 Created

表示资源已经创建。


4.2 3xx:重定向或缓存相关

常见:

301 Moved Permanently
302 Found
304 Not Modified
307 Temporary Redirect
308 Permanent Redirect

其中 304 Not Modified 非常重要。

它并不是:

请求失败

而是告诉客户端:

你之前缓存的内容仍然有效,可以继续使用。


4.3 4xx:客户端请求存在问题

常见:

400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
405 Method Not Allowed
409 Conflict
422 Unprocessable Content
429 Too Many Requests

尤其容易混淆:

401 ≠ 403

通常可以理解为:

401:当前请求缺少有效认证凭据
403:服务器理解了请求,但拒绝执行

例如 API:

没有 Token
    ↓
401

Token 有效,但没有权限
    ↓
403

4.4 5xx:服务器端错误

常见:

500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout

在微服务架构中:

Client
  ↓
Nginx
  ↓
Gateway
  ↓
Service A
  ↓
Service B

如果 Service A 调用 Service B 超时,就可能最终表现为:

504 Gateway Timeout

因此 HTTP 状态码不一定意味着错误直接发生在当前这一层。


5. HTTP/1.1 为什么需要 Keep-Alive?

早期 HTTP 通信可以简单理解成:

建立连接
 ↓
发送请求
 ↓
返回响应
 ↓
关闭连接

如果一个网页需要请求:

HTML
CSS
JS
图片
接口

每一个请求都重新建立连接,会产生大量连接建立开销。

因此 HTTP/1.1 支持持久连接。

可以理解为:

TCP Connection
│
├── HTTP Request 1
├── HTTP Response 1
│
├── HTTP Request 2
├── HTTP Response 2
│
├── HTTP Request 3
├── HTTP Response 3
│
└── ...

这样 TCP 连接可以被重复利用。

HTTP/1.1 的连接管理和消息解析由 RFC 9112 定义,其中包括持久连接以及消息长度、Chunked Transfer 等机制。


Chunked Transfer

当服务端无法提前知道完整响应长度时,可以使用:

Transfer-Encoding: chunked

例如:

chunk 1
chunk 2
chunk 3
...

服务端可以边生成数据边发送。

这在:

大文件
流式响应
动态内容

等场景比较有意义。

需要注意:

Content-Length

和:

Transfer-Encoding: chunked

是 HTTP/1.1 消息 framing 中不同的机制,不应该简单理解成“一个是普通传输,一个是流式传输”。


6. HTTP/2 解决了什么问题?

HTTP/1.1 的一个典型限制是:

一个连接
    ↓
多个请求
    ↓
请求之间存在排队和阻塞问题

HTTP/2 对 HTTP 的“线上表达方式”进行了重大改进。

核心特性包括:

二进制分帧
多路复用
Header 压缩

6.1 二进制分帧

HTTP/1.1 的消息格式具有明显的文本结构。

HTTP/2 则把通信拆成二进制 Frame。

大致可以理解:

HTTP Message
     ↓
Stream
     ↓
多个 Frame

6.2 多路复用

这是 HTTP/2 最重要的特性之一。

HTTP/1.1:

Connection
│
├── Request A
├── Response A
├── Request B
└── Response B

HTTP/2:

一个 TCP 连接
│
├── Stream 1
│    ├── Frame
│    └── Frame
│
├── Stream 3
│    ├── Frame
│    └── Frame
│
└── Stream 5
     ├── Frame
     └── Frame

多个 HTTP 请求可以在同一个连接中并发交错传输。

因此:

HTTP/1.1
多个请求 → 多连接/排队

HTTP/2
多个请求 → 一个连接中的多个 Stream

HTTP/2 规范明确将多路复用和字段压缩作为降低延迟、提高网络资源利用率的重要机制。

不过需要注意一个容易被忽略的问题:

HTTP/2 并没有消除 TCP 本身的队头阻塞问题。

因为多个 HTTP/2 Stream 最终仍然共享 TCP 字节流。

HTTP/3 使用 QUIC 后,在传输层进一步改变了这一点。

因此可以粗略理解为:

HTTP/1.1
   ↓
多个 HTTP 请求

HTTP/2
   ↓
多个 Stream
   ↓
共享 TCP

HTTP/3
   ↓
多个 Stream
   ↓
QUIC
   ↓
UDP

7. Cookie、Session 和 HTTP 缓存

HTTP 的一个重要特点是:

HTTP 本身是无状态的。

也就是说,服务器不能仅仅根据:

TCP Connection

就假设两个请求一定来自同一个用户。

因此 Web 应用需要额外机制维护状态。

服务端可以通过:

Set-Cookie: session_id=abc123

告诉浏览器保存 Cookie。

之后浏览器请求时:

Cookie: session_id=abc123

于是服务器就可以根据这个标识找到对应 Session。

简单理解:

第一次请求
Client ────────> Server

             Set-Cookie
Client <──────── Server


后续请求

Cookie
Client ────────> Server

Cookie 本质上是 HTTP 状态管理机制的一部分,并不是 Session 本身。经典 Cookie 规范 RFC 6265 定义了 Cookie 和 Set-Cookie 的基本机制。

实际开发中还需要关注:

HttpOnly
Secure
SameSite
Domain
Path
Expires / Max-Age

例如:

Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax

Session

Session 通常是服务端维护的状态:

Cookie
   ↓
session_id
   ↓
Server
   ↓
Session Data

例如:

session_id = abc123

Server:
abc123 → user_id=1001

所以:

Cookie ≠ Session

Cookie 是客户端保存并回传的数据。

Session 是应用服务端维护的会话状态。

现代 API 也可能采用:

Authorization: Bearer <token>

而不是传统 Session。


HTTP Cache

HTTP 缓存可以让客户端、代理服务器等复用之前的响应,从而减少网络请求和服务器压力。

例如:

第一次请求
Client ───────> Server
       <────── Response

保存缓存


第二次请求
Client ───────> Cache
       <────── Cached Response

或者通过条件请求:

If-None-Match: "abc123"

服务端发现资源没有变化:

HTTP/1.1 304 Not Modified

客户端继续使用已有缓存。

HTTP 缓存机制由 RFC 9111 定义,其中包括 Cache-Control、验证以及缓存复用等规则。

后端开发中经常见到:

Cache-Control: max-age=3600
ETag: "abc123"
Last-Modified: ...

这些 Header 并不是“浏览器自己的缓存功能”,而是 HTTP 标准定义的一套缓存控制机制。


8. HTTP、Socket、TCP、HTTPS、WebSocket 到底是什么关系?

这几个概念经常混在一起,可以从层次上理解。

应用
│
├── HTTP
├── WebSocket
└── 其他应用层协议
       │
       ▼
    Socket API
       │
       ▼
      TCP
       │
       ▼
       IP

但需要注意:

Socket 不是协议。

Socket 更像是应用程序访问网络通信能力的一种接口/抽象。

例如:

PHP
Java
Go
C

都可以通过 Socket API 使用 TCP/UDP 网络能力。

HTTP 则是建立在这些网络能力之上的应用层协议。


HTTPS

HTTPS 可以简单理解成:

HTTP
  +
TLS

TLS 负责:

加密
完整性保护
身份认证

所以:

HTTP
 ↓
TLS
 ↓
TCP
 ↓
IP

HTTPS 并不是一个完全不同于 HTTP 的应用层协议。

它主要是在 HTTP 与传输层之间增加了安全保护。


WebSocket

WebSocket 也是应用层协议。

它与 HTTP 有一个非常重要的区别:

HTTP
Client → Request
Server → Response

WebSocket
Client ⇄ Server

建立连接之后,可以进行持续的双向通信。

因此非常适合:

在线聊天
实时通知
实时协作
游戏
行情推送

9. 后端开发中如何理解一次 HTTP 请求?

作为后端开发者,可以把一次 API 请求抽象成:

Client
  │
  │ DNS
  ▼
IP
  │
  │ TCP / QUIC
  ▼
Load Balancer / Nginx
  │
  │ HTTP
  ▼
Application
  │
  ├── Router
  ├── Middleware
  ├── Authentication
  ├── Controller
  ├── Service
  ├── Database
  └── Redis
  │
  ▼
HTTP Response

例如:

POST /api/orders
Authorization: Bearer xxx
Content-Type: application/json

{
    "product_id": 1001,
    "quantity": 2
}

服务端可能经历:

Nginx
 ↓
HTTP Server
 ↓
Router
 ↓
Auth Middleware
 ↓
Controller
 ↓
Order Service
 ↓
MySQL
 ↓
Redis
 ↓
Response

最后:

HTTP/1.1 201 Created
Content-Type: application/json

{
    "id": 10001,
    "status": "created"
}

因此,学习 HTTP 的真正目的并不是记住大量 Header。

而是建立这样一条完整链路:

URL
 ↓
DNS
 ↓
TCP / QUIC
 ↓
TLS
 ↓
HTTP Request
 ↓
Web Server
 ↓
Application
 ↓
HTTP Response
 ↓
Browser / Client

当线上出现问题时,就可以逐层排查:

域名解析问题?
        ↓
TCP 连接问题?
        ↓
TLS 握手问题?
        ↓
HTTP 请求问题?
        ↓
网关问题?
        ↓
应用问题?
        ↓
数据库问题?
        ↓
响应问题?

这比单纯看到:

502
504
500

然后直接修改代码有效得多。


10. 总结

HTTP 是 Web 世界最核心的应用层协议之一,它采用请求/响应模型,并通过 Method、URI、Header、Body、Status Code 等元素表达通信语义。

从整体链路来看:

浏览器
  ↓
DNS
  ↓
TCP / QUIC
  ↓
TLS(HTTPS)
  ↓
HTTP
  ↓
Web Server
  ↓
Application
  ↓
HTTP Response

HTTP/1.1 重点解决了持久连接、消息传输和连接管理等问题;HTTP/2 进一步引入二进制分帧、多路复用和 Header 压缩;HTTP/3 则使用 QUIC 改变底层传输方式。

几个容易混淆的概念,可以最后再记一次:

TCP
    → 可靠的字节流传输

Socket
    → 应用程序访问网络通信的接口/抽象

HTTP
    → 应用层请求/响应协议

HTTPS
    → HTTP + TLS 安全保护

Cookie
    → 客户端保存并回传的状态信息

Session
    → 服务端维护的会话状态

HTTP/2
    → HTTP 语义 + 二进制分帧/多路复用等机制

HTTP/3
    → HTTP 语义 + QUIC 传输

真正理解 HTTP 后,再去学习 Nginx、API 网关、REST API、RPC、微服务、WebSocket、CDN 和浏览器网络性能优化,会容易很多。

另一篇 《HTTPS 详解:TLS 如何保护 HTTP 通信》

参考资料