简介
HTTP 协议构成了互联网的基础。它不仅负责传输网页、图片、视频等各种类型的资源,还为会话管理(Cookie)、认证(JWT)、缓存控制(Cache-Control)以及部分数据传输(Content-Range)提供支持。简单来说,HTTP 协议就像一个邮递系统:文件和代码作为信件,通过 HTTP 报文(信封)被封装,从服务器出发,经由多个代理和网络层层传递,最终到达用户设备,并在浏览器中呈现出来。对于后端开发者来说,理解 HTTP 协议不仅有助于编写 API,还能优化系统性能和安全性。
本文主要介绍 HTTP 协议的基础知识,包括协议的工作流程、报文格式、MIME 类型以及请求和响应的构成。至于下层网络栈和 HTTP 历史的更多细节,读者可以参考 Wikipedia 上关于 HTTP 和 TCP/IP 的条目。
HTTP 协议的工作流程
HTTP 协议在客户端-服务器模型中实现请求和响应功能,分为以下过程:
-
请求发起: 浏览器(客户端)向服务器发送 HTTP 请求。请求中包含请求行(方法、URL、协议版本)、请求头(例如 Host、User-Agent、Accept 等)以及可选的请求体(如在 POST 或 PUT 请求中)。
-
服务器处理: 服务器接收请求,解析请求报文,确定所需资源或操作。根据请求内容,服务器处理业务逻辑、检索数据或执行相应操作。
-
响应返回: 服务器生成 HTTP 响应,包括状态行(协议版本、状态码、原因短语)、响应头(例如 Content-Type、Content-Length、Cache-Control 等)和响应体(实际数据)。
-
客户端处理: 响应通过网络回传给客户端,浏览器对响应体进行解析和渲染,从而展现给用户。
此外,HTTP 协议作为应用层协议,在网络协议栈中处于顶层。下层协议(如 TCP/IP)负责数据传输的封装和路由,而 HTTP 协议只关注如何描述和传递资源及元数据。
为了更直观地理解,可以将整个过程比作寄送信件:
-
正文:代表网页内容或数据。
-
信封:即 HTTP 报文,其中包含地址(Host 字段)、发送方式(方法)以及其他辅助信息。
-
邮局:比喻为网络协议栈(TCP/IP),负责将信件从寄件人传递到收件人。
HTTP 发展历程
-
HTTP/0.9: 极其简化,仅支持GET方法,传输HTML文档,无头部和状态码,用于早期简单的Web信息获取。
-
HTTP/1.0: 引入了头部和状态码,支持POST和HEAD方法,可传输更多内容类型,但默认短连接。
-
HTTP/1.1: 确立持久连接为默认,加入分块传输、内容协商、缓存控制等关键特性,方法和状态码大幅扩展,成为现代Web的基础。
-
HTTP/2: 以性能为核心,采用二进制分帧和多路复用,加入头部压缩和服务器推送,显著提升效率和降低延迟,但仍基于TCP。
-
HTTP/3: 革新传输层,基于QUIC协议,彻底解决队头阻塞,实现0-RTT连接、连接迁移等特性,进一步优化移动网络环境下的性能,方法和核心语义与HTTP/2基本一致。
HTTP协议报文
HTTP 报文是 HTTP 协议传输信息的基本单位,分为请求报文和响应报文,两者都由 起始行, 头部(Headers) 以及 消息体(Body) 构成。
MIME 类型
当浏览器或其他客户端收到来自服务器的响应时,它们需要知道如何处理响应体中的数据。MIME 类型提供了这种信息。例如,如果 Content-Type 是 text/html,浏览器就知道这是一个 HTML 文档,需要将其渲染成网页。如果 Content-Type 是 image/jpeg,浏览器就知道这是一个 JPEG 图像,需要将其显示为图片。
在 HTTP 中,MIME 类型主要用于两个地方:
Content-Type响应头: 服务器在响应中通过Content-Type头告诉客户端响应体中数据的 MIME 类型。Accept请求头: 客户端在请求中通过Accept头告诉服务器自己可以接受的 MIME 类型。
MIME 类型通常采用 type/subtype 的形式,其中:
type:表示数据类型的大类,例如text(文本)、image(图像)、audio(音频)、video(视频)、application(应用程序)、multipart(多个部分)。subtype:表示更具体的数据格式,例如text/html、image/jpeg、application/json。
MIME 类型还可以包含可选的参数,用于提供关于数据的更多信息,例如字符编码。参数以分号 ( ;) 分隔,格式为 parameter=value。例如:text/html; charset=utf-8 表示这是一个 HTML 文档,使用 UTF-8 字符编码。
常见的MIME:
| MIME | 文件类型 |
|---|---|
| text/html | HTML 文档 |
| image/jpeg | JPEG 图像 |
| audio/wav | WAV 音频 |
| application/json | JSON 数据 |
| application/xml | XML 数据 |
| multipart/form-data | 用于表单提交,可以包含文件 |
| multipart/byteranges | 用于HTTP 范围请求 |
HTTP请求
HTTP 请求由以下几个部分组成:
-
请求行 (Request Line): 1. 方法 (Method): 指示要执行的操作。常见的 HTTP 方法包括:
GET: 获取资源。POST: 提交数据(例如,表单提交)。PUT: 更新资源(完整替换)。PATCH: 更新资源(部分修改)。DELETE: 删除资源。HEAD: 获取资源的元信息(类似于 GET,但不返回响应体)。OPTIONS: 获取服务器支持的请求方法。TRACE: 回显服务器收到的请求,主要用于测试或诊断。CONNECT: 建立一个到由请求目标标识的服务器的隧道。 2. URL (Uniform Resource Locator): 要请求的资源的地址。例如:https://www.example.com/page1.html3. HTTP 版本: 通常是HTTP/1.1,也可能是HTTP/2或HTTP/3。
-
请求头 (Request Headers): - 包含关于请求的附加信息,如客户端类型、接受的内容类型、语言偏好等。 - 常见的请求头:
Host: 指定服务器的域名和端口号。User-Agent: 标识客户端类型(浏览器、操作系统等)。Accept: 告知服务器客户端可以接受的内容类型。Accept-Language: 告知服务器客户端偏好的语言。Cookie: 包含之前服务器设置的 Cookie。Content-Type: 当请求体存在时,指定请求体的类型(如application/json)。Content-Length: 请求体的长度(字节)。Authorization: 携带认证凭据If-Modified-Since和If-None-Match: 用于缓存验证
-
请求体 (Request Body): - 可选,包含要发送给服务器的数据。 - 通常在
POST或PUT请求中使用。 - 可以是各种格式,如 JSON、XML、表单数据等。
HTTP 请求方法的特性
安全方法(Safe Methods)
如果一个请求方法不会对服务器产生预期的副作用,则称其为安全方法。常见的安全方法包括 GET、HEAD、OPTIONS 和 TRACE。
虽然安全方法理论上只用于读取数据,但它们仍可能在后台产生诸如写入日志或计费等副作用。
与安全方法相反,POST、PUT、DELETE、CONNECT 和 PATCH 方法通常会修改服务器状态或产生其他效果,因此不被认为是安全的。
幂等性 (Idempotent Methods)
如果多次对同一资源发送相同请求,服务器的状态变化与只发送一次请求时相同,则称该方法为幂等方法。
GET、HEAD、OPTIONS、TRACE 本身就是安全的,因此天然是幂等的;PUT 和 DELETE 方法也被定义为幂等方法,因为重复相同的 PUT 或 DELETE 操作不会进一步改变服务器状态。
与此相对,POST、CONNECT 和 PATCH 方法通常不具备幂等性,重复提交可能会导致多次状态改变或产生重复操作(例如重复发送邮件)
缓存性 (Cacheable Methods)
如果一个请求方法的响应可以被缓存以供将来复用,则称其为可缓存方法。
GET、HEAD 以及部分情况下的 POST 是可缓存的;而 PUT、DELETE、CONNECT、OPTIONS、TRACE 和 PATCH 方法通常不缓存响应。
HTTP 响应
3. HTTP 响应 (Response)
服务器收到请求后,会返回一个 HTTP 响应,它由以下部分组成:
-
状态行 (Status Line):
- HTTP 版本: 与请求中的版本相同。
- 状态码 (Status Code): 一个三位数的数字,表示请求的结果。常见的状态码:
200 OK: 请求成功。301 Moved Permanently: 永久重定向。302 Found: 临时重定向。304 Not Modified: 资源未修改(用于缓存)。400 Bad Request: 客户端请求错误。401 Unauthorized: 需要身份验证。403 Forbidden: 服务器拒绝访问。404 Not Found: 资源未找到。500 Internal Server Error: 服务器内部错误。503 Service Unavailable: 服务器暂时不可用。
- 原因短语 (Reason Phrase): 对状态码的简短描述(例如,
OK、Not Found)。
-
响应头 (Response Headers):
- 包含关于响应的附加信息,如服务器类型、内容类型、长度、缓存控制等。
- 常见的响应头:
Content-Type: 指定响应体的类型(如text/html、application/json)。Content-Length: 响应体的长度(字节)。Server: 服务器软件的名称和版本。Date: 响应生成的时间。Set-Cookie: 设置 Cookie。Location: 用于重定向(3xx 状态码)。Cache-Control,Expires,ETag,Last-Modified: 缓存控制相关的头部
-
响应体 (Response Body):
- 包含服务器返回的实际数据。
- 可以是 HTML、JSON、XML、图片、视频等。
HTTP 报文示例
以浏览器访问本站为例
HTTP Request
当进入页面时,浏览器先和blog.huangyj.me的服务器建立TCP连接,进行TLS握手,最后向https://blog.huangyj.me发起GET请求:
GET / HTTP/3
Host: blog.huangyj.me
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br, zstd
DNT: 1
Sec-GPC: 1
Alt-Used: blog.huangyj.me
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i
TE: trailers
关键字段:
GET / HTTP/3:GET方法,请求根目录,使用HTTP3协议Host,User-Agent: 请求的是那个网址,使用的是什么浏览器Accept:接受的MIME类型Accept-Encoding: 接受的头部编码方式Alt-Used:指示备用使用的主机名,通常由 CDN 添加,用于识别请求关联的原始域名或优化路由。Upgrade-Insecure-Requests:告知服务器客户端是否偏好安全连接,建议将 HTTP 请求升级为 HTTPS 请求。Sec-Fetch-Dest:请求的目标类型,document代表HTML页面
HTTP Response
服务器在收到请求之后,找到对应的文件,向浏览器发送 HTTP 响应,并发送网站的内容:
HTTP/3 200
date: Fri, 07 Feb 2025 05:31:06 GMT
content-type: text/html; charset=utf-8
cf-ray: 90e0fde6df51f702-NRT
server: cloudflare
access-control-allow-origin: *
cache-control: public, max-age=0, must-revalidate
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
content-encoding: br
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=Jp1u2y5xi5qqgRBTMzbTm4SiYnneRgWUb%2F8A%2BAraRwM6td2vk9RJHEGIzZqvuhHbrE%2FJF%2BIiOL2QFNCU9OUbfoqzYjLUB%2FqSLUt5yjdRNMzW13CTb3DAeMveVLBtSa7G0mw%3D"}],"group":"cf-nel","max_age":604800}
nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
vary: Accept-Encoding
cf-cache-status: DYNAMIC
alt-svc: h3=":443"; ma=86400
data,content-type:日期,MIME类型access-control-allow-origin:CORS跨域访问控制,值为*表示所有来自所有网页的请求都能够跨域访问。cache-control:用于控制资源的缓存策略。服务器通过该字段告知客户端或中间缓存服务器如何缓存响应资源,例如设定缓存有效期(max-age)、禁止缓存(no-cache/no-store)、以及在使用缓存前是否需要验证(must-revalidate)等。x-content-type-options:控制客户端对于MIME类型的处理,nosniff`表示客户端不需要猜测MIME类型,使用响应中声明的类型即可。
其他和 HTTP 协议有关的内容
HTTP 会话管理
HTTP 协议是一种无状态协议,即每个请求都是独立的,不会自动保存客户端状态。为实现用户认证、购物车和个性化服务,服务器采用会话管理机制。Cookie 是实现会话管理的重要手段,其本质是 HTTP 报文头中的一组键值对。 服务器在响应登录或首次请求时,会在 HTTP 响应报文中通过“Set-Cookie”头发送 Cookie(例如包含唯一的 Session ID),浏览器收到后存储并在后续请求中自动在“Cookie”头中携带。这样,服务器就能通过解析 HTTP 请求报文中的 Cookie,关联多个请求,从而保持会话状态。这种方式简单高效,但也需要防范如会话劫持等安全风险。
HTTP 请求报文提交Cookie:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml
Cookie: sessionid=abc123; theme=light; lang=zh
HTTP 响应报文设置Cookie:
HTTP/1.1 200 OK
Date: Sun, 09 Feb 2025 13:00:00 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8
Set-Cookie: sessionid=abc123; Expires=Mon, 10 Feb 2025 13:00:00 GMT; Path=/; HttpOnly
CORS跨域访问
浏览器的同源策略要求,只有当协议、域名和端口均相同时,网页才能访问对应资源;这旨在防止恶意脚本跨域窃取数据。然而,实际应用中,前后端分离或调用第三方 API 等场景常需跨域访问。
CORS(跨源资源共享)正是为了解决这一问题而设计的。服务器通过在 HTTP 响应报文中添加特定的 CORS 标头(如 Access-Control-Allow-Origin、Access-Control-Allow-Methods 等)来授权跨域请求。当请求较复杂时,浏览器会先发送预检请求(OPTIONS 方法),确认服务器允许后再发送正式请求。这样既满足跨域数据传输需求,又在一定程度上确保了安全性,避免了任意跨域访问带来的风险。
请求端字段:
- Origin:浏览器自动添加,表示请求发起源(协议、域名、端口)。
- Access-Control-Request-Method:在预检请求(OPTIONS 请求)中指定实际请求将使用的方法。
- Access-Control-Request-Headers:在预检请求中列出实际请求中将使用的自定义头部字段。 响应端字段:
- Access-Control-Allow-Origin:指定允许跨域访问的来源,可以是具体域名或
*(但携带凭证时不能用*)。 - Access-Control-Allow-Methods:列出允许的 HTTP 方法(如 GET、POST、PUT 等)。
- Access-Control-Allow-Headers:列出允许客户端在实际请求中发送的自定义头部。
- Access-Control-Allow-Credentials:指明是否允许携带凭证(例如 Cookie),值为
true表示允许。