概述
HTTP (Hypertext Transfer Protocol,超文本传输协议) 是用于在网上传输数据的应用层协议。HTTP 采用请求-响应模型,其中客户端发送 HTTP 请求报文到服务器,服务器返回 HTTP 响应报文。本文档详细介绍 HTTP 请求报文的结构、各部分组成及其作用。
HTTP 请求报文的整体结构
一个完整的 HTTP 请求报文由以下四个部分组成:
请求行 (Request Line)
请求头字段 (Header Fields)
空行 (Empty Line)
请求体 (Request Body) [可选]
结构示意图
POST /users HTTP/1.1 ← 请求行
Host: example.com ← 请求头字段
Content-Type: application/json ← 请求头字段
Content-Length: 49 ← 请求头字段
Authorization: Bearer token123 ← 请求头字段
← 空行
{"name": "John", "email": "john@example.com"} ← 请求体
第一部分: 请求行 (Request Line)
定义
请求行是 HTTP 请求报文的第一行,包含三个关键信息,用单个空格分隔。
格式
<方法> <请求目标> <协议版本>
组成部分
1. HTTP 方法 (Method)
HTTP 方法指示对资源要执行的操作类型。
常用方法及作用:
| 方法 | 作用 | 安全性 | 幂等性 | 可缓存 |
|---|---|---|---|---|
| GET | 获取资源 | 是 | 是 | 是 |
| HEAD | 获取资源元数据(不返回响应体) | 是 | 是 | 是 |
| POST | 提交数据/创建资源 | 否 | 否 | 条件可缓存 |
| PUT | 更新/替换资源 | 否 | 是 | 否 |
| DELETE | 删除资源 | 否 | 是 | 否 |
| PATCH | 部分更新资源 | 否 | 否 | 否 |
| OPTIONS | 获取服务器支持的方法 | 是 | 是 | 否 |
| CONNECT | 建立隧道连接(用于代理) | 否 | 否 | 否 |
| TRACE | 回显请求(用于调试) | 是 | 是 | 否 |
重要特性说明:
-
安全方法 (Safe Methods): 不会修改服务器状态的方法
-
幂等方法 (Idempotent Methods): 多次执行相同请求产生相同效果的方法
-
可缓存方法 (Cacheable Methods): 响应可以被缓存的方法
示例:
GET /api/users HTTP/1.1
POST /api/users HTTP/1.1
PUT /api/users/123 HTTP/1.1
DELETE /api/users/123 HTTP/1.1
2. 请求目标 (Request Target)
请求目标指定要访问的资源,有四种形式:
a) 源形式 (Origin Form) - 最常用
GET /path/to/resource?query=value HTTP/1.1
-
包含绝对路径和可选的查询字符串
-
用于 GET、POST、HEAD、OPTIONS 等方法
-
需要配合 Host 头字段使用
b) 绝对形式 (Absolute Form)
GET https://example.com/path HTTP/1.1
-
包含完整的 URL
-
主要用于代理服务器
c) 授权形式 (Authority Form)
CONNECT example.com:443 HTTP/1.1
-
仅包含主机和端口
-
专用于 CONNECT 方法建立隧道
d) 星号形式 (Asterisk Form)
OPTIONS * HTTP/1.1
-
使用 * 表示整个服务器
-
仅用于 OPTIONS 方法
3. 协议版本 (Protocol Version)
指定使用的 HTTP 协议版本。
主要版本:
-
HTTP/0.9(1991) - 已废弃 -
HTTP/1.0(1996) - 已过时 -
HTTP/1.1(1997) - 当前标准,广泛使用 -
HTTP/2(2015) - 二进制协议,支持多路复用 -
HTTP/3(2022) - 基于 QUIC/UDP 的最新版本
示例:
GET /index.html HTTP/1.1
第二部分: 请求头字段 (Header Fields)
定义
请求头字段提供关于请求、客户端和请求体的附加信息。每个头字段由字段名、冒号、字段值组成。
格式
字段名: 字段值
特点
-
字段名不区分大小写
-
字段值通常不能包含 CRLF
-
多个值可用逗号分隔
-
HTTP/1.1 中除了 Host 外其他都是可选的
分类和常用头字段
1. 通用头字段 (General Headers)
适用于请求和响应的头字段。
| 头字段 | 作用 | 示例 |
|---|---|---|
| Date | 消息创建的日期和时间 | Date: Mon, 27 Jul 2009 12:28:53 GMT |
| Connection | 控制连接管理 | Connection: keep-alive 或 Connection: close |
| Cache-Control | 缓存指令 | Cache-Control: no-cache, no-store |
| Pragma | HTTP/1.0 的缓存控制(已过时) | Pragma: no-cache |
| Trailer | 指示消息尾部包含哪些头字段 | Trailer: Expires |
| Transfer-Encoding | 传输编码方式 | Transfer-Encoding: chunked |
| Upgrade | 请求升级到另一个协议 | Upgrade: websocket |
| Via | 代理服务器信息 | Via: 1.1 proxy.example.com |
2. 请求头字段 (Request Headers)
特定于请求的头字段。
| 头字段 | 作用 | 示例 |
|---|---|---|
| Host | 目标服务器的主机名和端口(HTTP/1.1 必需) | Host: www.example.com |
| User-Agent | 客户端软件信息 | User-Agent: Mozilla/5.0 |
| Accept | 客户端可接受的媒体类型 | Accept: text/html, application/json |
| Accept-Encoding | 客户端支持的内容编码 | Accept-Encoding: gzip, deflate, br |
| Accept-Language | 客户端偏好的语言 | Accept-Language: zh-CN, en;q=0.9 |
| Accept-Charset | 客户端支持的字符集(已废弃) | Accept-Charset: utf-8 |
| Referer | 请求来源页面的 URL | Referer: https://google.com |
| From | 用户的电子邮件地址 | From: user@example.com |
| If-Modified-Since | 条件请求,仅当资源修改后才返回 | If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT |
| If-None-Match | 条件请求,基于 ETag | If-None-Match: "686897696a7c876b7e" |
| If-Match | 条件请求,仅当 ETag 匹配时处理 | If-Match: "686897696a7c876b7e" |
| If-Unmodified-Since | 条件请求,仅当资源未修改时处理 | If-Unmodified-Since: Wed, 21 Oct 2015 07:28:00 GMT |
| If-Range | 部分请求的条件 | If-Range: "686897696a7c876b7e" |
| Range | 请求资源的部分内容 | Range: bytes=0-1023 |
| Max-Forwards | 请求可经过的最大代理数 | Max-Forwards: 10 |
| TE | 客户端可接受的传输编码 | TE: trailers, deflate |
| Expect | 客户端期望的服务器行为 | Expect: 100-continue |
3. 实体头字段 (Entity/Representation Headers)
描述请求体的元数据。
| 头字段 | 作用 | 示例 |
|---|---|---|
| Content-Type | 请求体的媒体类型 | Content-Type: application/json; charset=utf-8 |
| Content-Length | 请求体的字节长度 | Content-Length: 1234 |
| Content-Encoding | 请求体的编码方式 | Content-Encoding: gzip |
| Content-Language | 请求体的自然语言 | Content-Language: zh-CN |
| Content-Location | 请求体的备用位置 | Content-Location: /documents/foo.json |
| Content-Range | 部分内容的位置信息 | Content-Range: bytes 200-1000/5000 |
4. 认证和授权头字段
| 头字段 | 作用 | 示例 |
|---|---|---|
| Authorization | 客户端的认证凭证 | Authorization: Bearer eyJhbGc... |
| Proxy-Authorization | 代理服务器的认证凭证 | Proxy-Authorization: Basic dXNlcjpwYXNz |
| Cookie | 发送 Cookie 到服务器 | Cookie: sessionId=abc123; theme=dark |
5. 常见的自定义/扩展头字段
| 头字段 | 作用 | 示例 |
|---|---|---|
| X-Requested-With | 标识 AJAX 请求 | X-Requested-With: XMLHttpRequest |
| X-Forwarded-For | 客户端真实 IP(通过代理) | X-Forwarded-For: 203.0.113.195 |
| X-Forwarded-Host | 客户端请求的原始主机 | X-Forwarded-Host: example.com |
| X-Forwarded-Proto | 客户端使用的协议 | X-Forwarded-Proto: https |
| Origin | 请求的源(用于 CORS) | Origin: https://example.com |
| Access-Control-Request-Method | CORS 预检请求的方法 | Access-Control-Request-Method: POST |
| Access-Control-Request-Headers | CORS 预检请求的头字段 | Access-Control-Request-Headers: Content-Type |
完整示例
GET /api/users?page=1&limit=10 HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Referer: https://example.com/dashboard
Cookie: sessionId=abc123; theme=dark
Cache-Control: no-cache
第三部分: 空行 (Empty Line)
定义
空行是一个 CRLF (回车换行符,\r\n),用于分隔请求头和请求体。
作用
-
标记请求头部分的结束
-
告诉服务器元数据已完成,后续内容为请求体
-
HTTP/1.1 中必须存在,即使没有请求体
重要性
如果缺少空行:
-
服务器可能将请求体的内容误认为是头字段
-
可能导致请求解析错误
-
通常会返回 400 Bad Request 错误
示例
POST /api/data HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 27
← 这里是空行 (CRLF)
{"key":"value","id":123} ← 请求体从空行后开始
第四部分: 请求体 (Request Body)
定义
请求体是可选的,包含要发送到服务器的数据。不是所有请求都有请求体。
何时使用请求体
通常包含请求体的方法:
-
POST - 创建资源或提交表单数据
-
PUT - 更新/替换资源
-
PATCH - 部分更新资源
通常不包含请求体的方法:
-
GET - 虽然技术上可以,但不推荐
-
HEAD - 与 GET 相同,不应有请求体
-
DELETE - 可选,但通常不使用
-
OPTIONS - 通常不使用
-
TRACE - 不应有请求体
请求体的类型
1. application/x-www-form-urlencoded
用途: HTML 表单的默认编码类型
格式: key1=value1&key2=value2
示例:
POST /form HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
name=John+Doe&email=john%40example.com&age=30
特点:
-
键值对用
&分隔 -
空格编码为
+或%20 -
特殊字符需要 URL 编码
-
简单高效,适合简单表单
2. multipart/form-data
用途: 上传文件或提交包含二进制数据的表单
格式: 使用边界分隔符分割多个部分
示例:
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Length: 345
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"
John Doe
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="avatar"; filename="photo.jpg"
Content-Type: image/jpeg
[二进制图片数据]
------WebKitFormBoundary7MA4YWxkTrZu0gW--
特点:
-
每个部分有自己的头字段
-
可以混合文本和二进制数据
-
边界字符串不能出现在数据中
-
适合文件上传
3. application/json
用途: 现代 Web API 的标准数据格式
格式: JSON 对象
示例:
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json; charset=utf-8
Content-Length: 95
{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"age": 30
}
特点:
-
结构化数据
-
易于解析和生成
-
支持嵌套对象和数组
-
现代 RESTful API 的首选
4. application/xml 或 text/xml
用途: XML 格式数据传输
示例:
POST /api/data HTTP/1.1
Host: api.example.com
Content-Type: application/xml; charset=utf-8
Content-Length: 145
<?xml version="1.0" encoding="UTF-8"?>
<user>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>john.doe@example.com</email>
</user>
特点:
-
结构化标记语言
-
支持验证和命名空间
-
较 JSON 更冗长
-
传统企业应用常用
5. text/plain
用途: 纯文本数据
示例:
POST /log HTTP/1.1
Host: example.com
Content-Type: text/plain; charset=utf-8
Content-Length: 45
This is a simple text message without format.
6. application/octet-stream
用途: 任意二进制数据
示例:
POST /upload HTTP/1.1
Host: example.com
Content-Type: application/octet-stream
Content-Length: 10240
[二进制数据]
特点:
-
用于传输原始二进制数据
-
服务器通常会将其保存为文件
-
适合下载/上传文件
7. application/graphql
用途: GraphQL 查询
示例:
POST /graphql HTTP/1.1
Host: api.example.com
Content-Type: application/graphql
Content-Length: 78
query {
user(id: "123") {
name
email
posts {
title
}
}
}
请求体大小限制
-
服务器通常对请求体大小有限制
-
超过限制会返回 413 Payload Too Large
-
常见限制: 1MB - 50MB
-
可通过
Content-Length头字段预先告知大小
HTTP/1.1 完整请求示例
示例 1: 简单 GET 请求
GET /api/users/123 HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Accept: application/json
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0...
Cache-Control: no-cache
注意: GET 请求没有请求体,空行后直接结束
示例 2: POST 创建用户
POST /api/users HTTP/1.1
Host: api.example.com
User-Agent: MyApp/1.0
Content-Type: application/json; charset=utf-8
Content-Length: 123
Accept: application/json
Authorization: Bearer token123
Connection: keep-alive
{
"username": "johndoe",
"email": "john@example.com",
"password": "securepass123",
"role": "user"
}
示例 3: PUT 更新资源
PUT /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 89
Authorization: Bearer token123
If-Match: "686897696a7c876b7e"
{
"email": "newemail@example.com",
"role": "admin",
"active": true
}
示例 4: 文件上传
POST /api/upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----FormBoundary123
Content-Length: 4567
Authorization: Bearer token123
------FormBoundary123
Content-Disposition: form-data; name="description"
Profile picture upload
------FormBoundary123
Content-Disposition: form-data; name="file"; filename="avatar.jpg"
Content-Type: image/jpeg
[图片的二进制数据]
------FormBoundary123--
示例 5: 条件请求
GET /api/data/report.pdf HTTP/1.1
Host: api.example.com
User-Agent: DownloadManager/2.0
Accept: application/pdf
If-Modified-Since: Mon, 01 Jan 2024 00:00:00 GMT
If-None-Match: "abc123def456"
Range: bytes=0-1023
Connection: keep-alive
HTTP/2 的变化
主要区别
HTTP/2 将文本格式改为二进制帧格式,但语义相同。
伪头字段 (Pseudo-headers)
HTTP/2 用伪头字段替代请求行的信息,以 : 开头:
请求伪头字段:
-
:method- HTTP 方法 -
:scheme- 协议方案 (http 或 https) -
:authority- 主机名和端口(替代 Host 头) -
:path- 路径和查询字符串
HTTP/2 请求示例 (概念表示):
:method: GET
:scheme: https
:authority: api.example.com
:path: /users/123
user-agent: MyApp/1.0
accept: application/json
其他特性
-
二进制分帧: 消息被分割成帧
-
多路复用: 单个连接上的多个并发请求
-
头部压缩: 使用 HPACK 算法压缩头部
-
服务器推送: 服务器可主动推送资源
HTTP/3 的变化
基于 QUIC
HTTP/3 使用 QUIC 协议(基于 UDP)替代 TCP:
-
更快的连接建立
-
改进的拥塞控制
-
更好的移动网络支持
-
解决 TCP 队头阻塞问题
语义保持不变
HTTP/3 的请求报文结构和语义与 HTTP/2 基本相同,主要差异在传输层。
请求报文的处理流程
客户端侧
-
构建请求行 - 确定方法、目标 URI 和协议版本
-
添加必需头字段 - 如 Host (HTTP/1.1 必需)
-
添加可选头字段 - 如认证、缓存控制等
-
添加空行 - 标记头部结束
-
添加请求体 - 如果需要(POST、PUT 等)
-
发送到服务器 - 通过 TCP/IP 连接
服务器侧
-
接收请求 - 读取 TCP 流
-
解析请求行 - 提取方法、URI、协议版本
-
解析头字段 - 逐行读取,直到空行
-
读取请求体 - 根据 Content-Length 或 Transfer-Encoding
-
处理请求 - 根据方法和 URI 执行操作
-
生成响应 - 构建响应报文并发送
常见问题和最佳实践
1. 请求方法的选择
-
GET: 获取资源,不修改状态
-
POST: 创建资源或提交复杂数据
-
PUT: 完整替换资源
-
PATCH: 部分更新资源
-
DELETE: 删除资源
2. 头字段使用建议
-
必须设置 Host: HTTP/1.1 规范要求
-
明确 Content-Type: 指定请求体格式
-
设置 Content-Length: 或使用 chunked 编码
-
使用合适的 Accept: 告诉服务器期望的响应格式
-
添加 User-Agent: 便于服务器统计和问题排查
-
适当使用缓存控制: 提升性能
3. 安全性考虑
-
使用 HTTPS: 加密传输,防止窃听和篡改
-
不在 URL 中传递敏感信息: 使用请求体或认证头
-
验证输入: 防止注入攻击
-
使用强认证: OAuth 2.0、JWT 等
-
限制请求大小: 防止拒绝服务攻击
-
实现速率限制: 防止滥用
4. 性能优化
-
使用 Keep-Alive: 复用连接
-
启用压缩: gzip、br 等
-
合理使用缓存: 减少不必要的请求
-
HTTP/2 多路复用: 并行请求
-
减少头字段大小: 避免过大的 Cookie
5. 调试技巧
-
使用浏览器开发者工具: Network 标签
-
使用 curl 命令 :
curl -v https://example.com -
使用 Postman/Insomnia: API 测试工具
-
查看原始请求 :
curl -v --trace-ascii - -
代理工具: Charles、Fiddler、mitmproxy
相关标准文档
RFC 文档
-
RFC 9110: HTTP Semantics (2022) - HTTP 语义
-
RFC 9112: HTTP/1.1 (2022) - HTTP/1.1 协议
-
RFC 9113: HTTP/2 (2022) - HTTP/2 协议
-
RFC 9114: HTTP/3 (2022) - HTTP/3 协议
-
RFC 9111: HTTP Caching (2022) - HTTP 缓存
-
RFC 6265: HTTP State Management (Cookies) - Cookie 管理
-
RFC 7231: HTTP/1.1 Semantics (已被 RFC 9110 取代)
-
RFC 7540: HTTP/2 (已被 RFC 9113 取代)
相关资源
总结
HTTP 请求报文是客户端与服务器通信的基础,由四个部分组成:
-
请求行: 定义操作(方法)、目标(URI)和协议版本
-
请求头字段: 提供请求的元数据和上下文信息
-
空行: 分隔头部和消息体
-
请求体: 携带要发送的数据(可选)
理解 HTTP 请求报文的结构和各部分的作用,对于:
-
Web 开发和 API 设计
-
网络调试和问题排查
-
性能优化
-
安全防护
都至关重要。随着 HTTP/2 和 HTTP/3 的普及,虽然传输格式发生了变化,但核心语义保持一致,掌握 HTTP/1.1 的基础知识依然是理解现代 HTTP 协议的关键。
