当你打开浏览器,在地址栏输入一个网址并按下回车,几秒钟后网页就呈现在眼前。这个过程看似简单,背后却是HTTP协议在工作。无论是浏览网页、使用手机App,还是调用后台API,HTTP协议都在默默支撑着互联网的数据传输。理解HTTP,是理解Web世界的起点。
HTTP是什么
HTTP是HyperText Transfer Protocol的缩写,中文译为超文本传输协议。它是一种应用层协议,运行在TCP/IP协议栈之上,用于在Web浏览器和Web服务器之间传输数据。
1989年,Tim Berners-Lee在CERN(欧洲核子研究中心)提出了World Wide Web的概念。为了实现这个构想,他设计了四个核心组件:HTML(超文本标记语言)、HTTP(超文本传输协议)、Web浏览器和Web服务器。1991年8月6日,第一个网站上线,HTTP协议正式登上历史舞台。
HTTP的设计初衷非常简单:让客户端(通常是浏览器)能够从服务器获取文档。最初的HTTP/0.9版本只有一个GET方法,请求只有一行,响应只返回HTML内容,没有状态码,没有头部信息。随着Web的快速发展,HTTP逐渐演进,功能越来越丰富,但核心理念始终不变:客户端发送请求,服务器返回响应。
请求-响应模型
HTTP采用请求-响应(Request-Response)模型工作。这个模型可以概括为一句话:客户端发起请求,服务器返回响应。
┌─────────┐ ┌─────────┐
│ │ ──── Request ────▶ │ │
│ Client │ │ Server │
│ │ ◀─── Response ──── │ │
└─────────┘ └─────────┘
图片来源:基于HTTP协议规范绘制
整个过程是这样的:
-
客户端发送请求:用户在浏览器中输入URL或点击链接,浏览器构建HTTP请求消息,通过TCP连接发送给服务器。
-
服务器处理请求:服务器收到请求后,解析请求内容,根据请求方法、路径和头部信息决定如何处理。
-
服务器返回响应:服务器将处理结果封装成HTTP响应消息,包含状态码、响应头和响应体,发送回客户端。
-
客户端处理响应:浏览器收到响应后,根据状态码判断请求是否成功,解析响应体中的内容并渲染页面。
这个模型的一个关键特点是无状态(Stateless)。每次HTTP请求都是独立的,服务器不会"记住"之前的请求。这种设计简化了服务器实现,但也带来了挑战——比如用户登录后如何保持登录状态?解决方案是使用Cookie和Session,在请求中携带状态信息。
HTTP请求消息的结构
一个完整的HTTP请求消息由三部分组成:请求行(Request Line)、请求头(Request Headers) 和 请求体(Request Body)。
POST /api/users HTTP/1.1 ← 请求行
Host: example.com ← 请求头
Content-Type: application/json
Content-Length: 42
User-Agent: Mozilla/5.0
← 空行
{"name":"张三","email":"[email protected]"} ← 请求体
请求行
请求行包含三个信息,用空格分隔:
- 请求方法:如
GET、POST、PUT、DELETE等 - 请求目标(Request Target):通常是URL的路径部分,如
/api/users - HTTP版本:如
HTTP/1.1
请求头
请求头是键值对形式的关键信息,用于描述请求的元数据。每个头部字段占一行,格式为字段名: 字段值。常见的请求头包括:
| 头部字段 | 作用 | 示例 |
|---|---|---|
Host |
指定服务器域名和端口 | Host: www.example.com |
User-Agent |
客户端信息 | User-Agent: Mozilla/5.0 (Windows NT 10.0) |
Accept |
可接受的响应内容类型 | Accept: text/html,application/json |
Accept-Language |
可接受的语言 | Accept-Language: zh-CN,zh;q=0.9 |
Accept-Encoding |
可接受的编码方式 | Accept-Encoding: gzip, deflate |
Content-Type |
请求体的媒体类型 | Content-Type: application/json |
Content-Length |
请求体的长度(字节) | Content-Length: 42 |
Authorization |
认证信息 | Authorization: Bearer eyJhbGciOiJIUzI1NiIs... |
Cookie |
携带的Cookie | Cookie: session_id=abc123 |
请求体
请求体包含要发送给服务器的数据。并非所有请求都有请求体——GET请求通常没有请求体,而POST、PUT、PATCH请求通常包含请求体。
请求体的格式由Content-Type头部指定,常见格式包括:
application/json:JSON格式,API开发中最常用application/x-www-form-urlencoded:表单键值对,键值用=连接,多个键值用&分隔multipart/form-data:多部分表单数据,用于文件上传
HTTP请求方法详解
HTTP定义了一组请求方法(也称为HTTP动词),每个方法表示对资源的不同操作意图。
| 方法 | 作用 | 是否有请求体 | 是否幂等 | 是否安全 |
|---|---|---|---|---|
GET |
获取资源 | 否 | 是 | 是 |
POST |
创建资源 | 是 | 否 | 否 |
PUT |
更新资源(完整替换) | 是 | 是 | 否 |
PATCH |
更新资源(部分修改) | 是 | 否 | 否 |
DELETE |
删除资源 | 否 | 是 | 否 |
HEAD |
获取资源头部信息 | 否 | 是 | 是 |
OPTIONS |
查询支持的方法 | 否 | 是 | 是 |
幂等性和安全性
理解这两个概念对于正确使用HTTP方法非常重要:
安全性(Safe):指该方法不会修改服务器上的资源状态。GET、HEAD、OPTIONS是安全方法。安全方法可以被缓存、预取,不会产生副作用。
幂等性(Idempotent):指多次执行相同请求,结果与执行一次相同。GET、PUT、DELETE、HEAD、OPTIONS是幂等的。POST不是幂等的——两次相同的POST请求可能创建两个相同的资源。
常用方法详解
GET:最常用的方法,用于获取资源。请求参数通常放在URL的查询字符串中:
GET /api/users?page=1&size=10 HTTP/1.1
Host: api.example.com
POST:用于创建新资源或提交数据。数据放在请求体中:
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"name":"李四","email":"[email protected]"}
PUT:用于完整更新资源。需要提供资源的完整数据:
PUT /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"name":"王五","email":"[email protected]","age":25}
PATCH:用于部分更新资源。只提供需要修改的字段:
PATCH /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"age":26}
DELETE:用于删除资源:
DELETE /api/users/123 HTTP/1.1
Host: api.example.com
HTTP响应消息的结构
HTTP响应消息同样由三部分组成:状态行(Status Line)、响应头(Response Headers) 和 响应体(Response Body)。
HTTP/1.1 200 OK ← 状态行
Content-Type: application/json ← 响应头
Content-Length: 85
Cache-Control: max-age=3600
← 空行
{"id":123,"name":"张三","email":"[email protected]"} ← 响应体
状态行
状态行包含三个信息:
- HTTP版本:如
HTTP/1.1 - 状态码:三位数字,表示请求的处理结果,如
200 - 原因短语:状态码的简短描述,如
OK
响应头
响应头同样以键值对形式提供元数据。常见的响应头包括:
| 头部字段 | 作用 | 示例 |
|---|---|---|
Content-Type |
响应体的媒体类型 | Content-Type: text/html; charset=utf-8 |
Content-Length |
响应体的长度 | Content-Length: 1024 |
Content-Encoding |
响应体的编码方式 | Content-Encoding: gzip |
Cache-Control |
缓存控制指令 | Cache-Control: max-age=3600 |
Set-Cookie |
设置Cookie | Set-Cookie: session_id=xyz; HttpOnly; Secure |
Location |
重定向目标地址 | Location: https://example.com/new |
Server |
服务器信息 | Server: nginx/1.18.0 |
响应体
响应体是服务器返回的实际数据。对于HTML页面,响应体是HTML代码;对于API请求,响应体通常是JSON数据。某些响应(如204 No Content)没有响应体。
HTTP状态码详解
状态码是HTTP协议的重要组成部分,它告诉客户端请求的处理结果。状态码是一个三位数字,第一位数字表示状态码的类别。
状态码分类
| 类别 | 范围 | 含义 |
|---|---|---|
| 1xx | 100-199 | 信息性响应——请求已被接收,正在处理 |
| 2xx | 200-299 | 成功——请求已成功接收、理解并接受 |
| 3xx | 300-399 | 重定向——需要客户端采取进一步操作 |
| 4xx | 400-499 | 客户端错误——请求包含语法错误或无法完成 |
| 5xx | 500-599 | 服务器错误——服务器无法完成有效请求 |
常见状态码详解
成功响应(2xx):
200 OK:请求成功。最常用的成功状态码。201 Created:创建成功。通常用于POST请求后返回。204 No Content:成功但无返回内容。常用于DELETE请求。206 Partial Content:部分内容。用于断点续传或分块下载。
重定向(3xx):
301 Moved Permanently:资源已永久移动到新地址。浏览器会缓存新地址。302 Found:资源临时移动。常用于临时重定向。304 Not Modified:资源未修改。配合缓存使用,告诉客户端可以使用缓存的版本。
客户端错误(4xx):
400 Bad Request:请求语法错误。客户端需要修改请求内容。401 Unauthorized:需要身份认证。需要在请求中携带认证信息。403 Forbidden:禁止访问。服务器拒绝执行请求。404 Not Found:资源不存在。最广为人知的状态码。405 Method Not Allowed:请求方法不被允许。429 Too Many Requests:请求过于频繁,触发限流。
服务器错误(5xx):
500 Internal Server Error:服务器内部错误。最常见的服务器错误。502 Bad Gateway:网关错误。代理服务器从上游服务器收到无效响应。503 Service Unavailable:服务暂时不可用。通常是因为服务器过载或正在维护。504 Gateway Timeout:网关超时。代理服务器等待上游服务器响应超时。
HTTP与HTTPS
HTTP协议传输的数据是明文的,任何人都可以在网络中截获并查看内容。这在涉及敏感信息(如密码、银行卡号)时非常危险。HTTPS应运而生。
HTTPS是HTTP Secure的缩写,它在HTTP和TCP之间加入了一层TLS/SSL加密协议。简单来说,HTTPS = HTTP + TLS/SSL。
HTTPS的工作原理
HTTPS的核心是TLS握手过程:
- 客户端发起连接:客户端发送支持的加密套件列表。
- 服务器响应:服务器选择一个加密套件,并发送数字证书。
- 证书验证:客户端验证证书的有效性(是否由可信CA签发、是否过期、域名是否匹配)。
- 密钥交换:客户端生成会话密钥,用服务器的公钥加密后发送给服务器。
- 建立加密通道:双方使用会话密钥进行对称加密通信。
HTTP与HTTPS的对比
| 对比项 | HTTP | HTTPS |
|---|---|---|
| 端口 | 80 | 443 |
| 数据传输 | 明文 | 加密 |
| 安全性 | 不安全,易被窃听和篡改 | 安全,数据加密传输 |
| 证书 | 不需要 | 需要数字证书 |
| 性能 | 略快 | 略慢(TLS握手开销) |
现代Web开发中,HTTPS已成为标配。浏览器会对HTTP网站标记"不安全",搜索引擎也会优先收录HTTPS网站。
HTTP缓存机制
缓存是提升Web性能的重要手段。HTTP提供了完善的缓存控制机制,分为强缓存和协商缓存两种。
强缓存
强缓存是指浏览器直接使用本地缓存,不向服务器发送请求。通过响应头控制:
Expires:指定资源的过期时间(绝对时间)。缺点是依赖客户端时间。Cache-Control: max-age=秒数:指定资源在多少秒内有效。优先级高于Expires。
Cache-Control: max-age=3600
这表示资源在3600秒(1小时)内有效,期间浏览器直接使用缓存,不发送请求。
协商缓存
当强缓存失效后,浏览器会向服务器发送请求,验证缓存是否仍然有效。这叫协商缓存。
Last-Modified / If-Modified-Since:
服务器在响应中返回Last-Modified头部,表示资源的最后修改时间:
Last-Modified: Wed, 08 Mar 2026 10:00:00 GMT
浏览器下次请求时,携带If-Modified-Since头部:
If-Modified-Since: Wed, 08 Mar 2026 10:00:00 GMT
服务器比较时间:如果资源未修改,返回304 Not Modified(无响应体);如果已修改,返回新内容和200 OK。
ETag / If-None-Match:
ETag是资源的唯一标识符(通常是内容哈希值),比Last-Modified更精确:
ETag: "abc123"
浏览器下次请求时携带If-None-Match:
If-None-Match: "abc123"
服务器比较ETag:匹配则返回304,不匹配则返回新内容。
ETag优先级高于Last-Modified,因为Last-Modified只能精确到秒,且内容不变但修改时间变化时会产生误判。
HTTP协议版本演进
HTTP协议经历了多次重大演进,每个版本都解决了上一版本的问题。
HTTP/0.9(1991年)
最初的版本,极为简单:
- 只有
GET方法 - 没有请求头和响应头
- 只能传输HTML
- 没有状态码
HTTP/1.0(1996年)
增加了重要特性:
- 引入
POST和HEAD方法 - 引入请求头和响应头
- 引入状态码
- 支持
Content-Type,可传输任意类型数据 - 每次请求需要新建TCP连接,完成后关闭
HTTP/1.1(1997年)
最广泛使用的版本,引入了关键改进:
- 持久连接:
Connection: keep-alive,一个TCP连接可以传输多个请求 - 管道化:可以连续发送多个请求,无需等待响应(但实际支持不佳)
- Host头部:支持虚拟主机,一个IP地址可以托管多个域名
- 分块传输:
Transfer-Encoding: chunked,支持动态生成内容 - 新增
PUT、DELETE、OPTIONS等方法
HTTP/1.1存在队头阻塞问题:即使使用持久连接,请求必须按顺序处理,一个慢请求会阻塞后续请求。
HTTP/2(2015年)
为解决性能问题而设计:
- 二进制分帧:消息以二进制帧形式传输,更高效
- 多路复用:一个TCP连接可并行处理多个请求/响应,解决队头阻塞
- 头部压缩:HPACK算法压缩头部,减少重复传输
- 服务器推送:服务器可主动推送资源到客户端
HTTP/3(2022年)
基于QUIC协议的最新版本:
- 基于UDP:放弃TCP,使用QUIC协议(基于UDP实现可靠传输)
- 解决TCP层队头阻塞:丢包只影响单个流,不会阻塞其他请求
- 更快的连接建立:0-RTT连接建立,减少握手延迟
- 内置TLS 1.3:加密成为强制要求
实践建议
理解HTTP协议对日常开发很有帮助:
设计API时:遵循RESTful原则,正确使用HTTP方法和状态码。用GET获取资源,POST创建资源,PUT完整更新,PATCH部分更新,DELETE删除。返回正确的状态码,让客户端能正确处理响应。
处理缓存时:合理设置Cache-Control和ETag,减少重复请求,提升性能。静态资源使用强缓存,动态资源使用协商缓存。
处理安全时:始终使用HTTPS。设置安全头部如Content-Security-Policy、X-Frame-Options、Strict-Transport-Security。Cookie设置HttpOnly和Secure属性。
调试时:善用浏览器开发者工具的Network面板,查看请求响应的详细信息。使用curl命令行工具测试API。
参考资料: