自在学

我们与你共同进步

  • 分类课程
  • 文章
  • 工作台
  • 订阅

  • 关于我们
  • 隐私政策
  • 使用条款

探索

  • 分类课程
  • 文章
  • 工作台
  • 订阅

网站信息

  • 关于我们
  • 隐私政策
  • 使用条款

加入社区

自在学学习社区微信二维码

微信扫码,交流学习

株洲市自在学教育科技有限公司© 2025 - 2026 版权所有

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

湘公网安备43020302000292号|湘ICP备2025148919号-1
分类课程工作台文章订阅
分类课程工作台文章价格

Web后端基础原理

  1. 01HTTP 概览
  2. 02URL
  3. 03HTTP消息
  4. 04连接管理
  5. 05Web 服务器
  6. 06代理
  7. 07缓存
  8. 08集成点
  9. 09Web认证
  10. 10Secure HTTP
  11. 11HTTP实体
  12. 12国际化
  13. 13负载均衡
正在加载课程章节内容
课程编程Web后端基础原理HTTP 概览

HTTP 概览

你在浏览器地址栏里输入一个网址,按下回车,几百毫秒后页面就出现了。看起来像是浏览器去服务器“拿了一次网页”。

可你打开开发者工具的 Network 面板,往往会看到另一幅景象:HTML 是一次请求,CSS 是一次请求,JavaScript 是一次请求,图片和字体各有各的请求,页面运行后还可能继续请求 JSON 数据。一个看似完整的页面,背后可能是几十次甚至几百次彼此独立的通信。

更反直觉的是,这些通信使用的 HTTP 并不负责把页面画出来,也不负责找到服务器的 IP 地址,更不保证服务器一定保存了你上次访问时的状态。HTTP 主要规定的是:谁向谁表达什么意图,对方用什么结果回答,以及双方怎样描述传输的数据。

这正是后端开发者需要掌握的第一张地图。路由、接口、状态码、Cookie、缓存、网关和负载均衡看起来是不同主题,往下追一层,都会落回 HTTP 的请求、响应、资源和连接。

从地址栏回车到完整页面呈现的 HTTP 全景图


HTTP 到底管什么

HTTP 的全称是 Hypertext Transfer Protocol,中文通常译为“超文本传输协议”。名字里虽然有“超文本”,它今天传输的内容早已不限于 HTML。图片、音频、视频、JSON、压缩包,甚至一段没有正文的控制信息,都可以放进 HTTP 消息里。

先把“协议”这个词说得具体一点。协议不是某个程序,也不是某台服务器。它是一套通信约定。客户端用 GET 表达“读取”,服务器用 200 表达“请求成功”,双方用 Content-Type 说明正文该按什么格式理解。这些约定让浏览器、命令行工具、Java 后端和 Go 网关即使由不同团队开发,也能互相听懂。

它是应用层协议

当你发送一个 HTTP 请求时,数据还需要借助更下层的能力在网络中移动。域名通常先经过 DNS 解析,数据包依靠 IP 找路,HTTP/1.1 和 HTTP/2 通常借助 TCP 可靠传输,HTTPS 还要使用 TLS 保护通信;HTTP/3 则把 HTTP 语义映射到基于 UDP 的 QUIC 上。

所以,“HTTP 使用 TCP”只对一部分版本成立。更准确的说法是:HTTP 定义应用层语义,再由具体版本选择合适的消息格式和底层传输方式。

排查问题时要分层。域名解析失败不等于 HTTP 返回了错误,TCP 连接超时也不等于服务器返回了 504。只有收到格式正确的 HTTP 响应,才有 HTTP 状态码可谈。

它用请求和响应表达一次交互

HTTP 的基本节奏是客户端先发送请求,服务器再给出响应。请求说明目标资源、操作意图和附加条件;响应说明处理结果,并可能带回资源的一种表示。

例如,浏览器想读取商品列表,可以发出这样的意图:

http
GET /products?category=book HTTP/1.1
Host: shop.example.test
Accept: application/json

服务器可能回答:

http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=60
 
{"items":[{"id":42,"name":"网络入门"}]}

这里真正稳定的是语义:读取 /products 指向的资源,筛选 book 类别,希望得到 JSON,最终成功返回。HTTP/2 和 HTTP/3 在网络上传输它时不会原样发送这段文本,但方法、目标、字段、状态码和内容仍表达同一件事。

“无状态”不是“服务器不能保存数据”

你登录之后刷新页面,网站仍然知道你是谁。那为什么 HTTP 还被称为无状态协议?

因为 HTTP 的“无状态”说的是:每个请求的语义都应当能够独立理解,不能仅凭它与上一条消息共用某条连接,就推断它属于同一个用户或某个固定业务步骤。

应用当然可以保存状态。服务器可以把会话记录放进数据库或缓存,浏览器也可以保存 Cookie。后续请求带上会话标识、访问令牌或其他凭证,应用就能把这次请求关联到某个用户。状态是应用机制显式带回来的,不是 HTTP 连接天然记住的。

这样设计的收益很现实:代理可以复用连接,负载均衡器也可以把不同请求分给不同服务器。代价是,需要连续上下文的业务必须自己设计会话、认证和状态存储,不能把“刚才处理过谁”寄托在连接上。

无状态也不等于任意请求都可以放心重试。一次 POST 可能已经创建订单,只是响应在回程中丢了。是否能重试,要继续判断方法语义、幂等性和业务去重机制。


客户端、服务器和中间节点

很多初学者会把“客户端”理解成自己的电脑,把“服务器”理解成机房里的一台机器。这个理解在画示意图时够用,写真实后端时却容易出错。

HTTP 中的客户端和服务器是一次通信里的角色。发起请求的程序是客户端,接收请求并返回响应的程序是服务器。同一个程序可以在不同连接里扮演不同角色:你的订单服务对浏览器是服务器,但它请求支付服务时,又成了客户端。

客户端不只有浏览器

浏览器是最常见的客户端,也叫用户代理。它除了发送 HTTP 请求,还会解析 HTML、执行 JavaScript、应用 CSS、管理缓存和 Cookie,并把多个资源拼成用户看到的页面。

但 curl、手机 App、搜索引擎爬虫、自动化测试、智能设备和另一个后端服务都可以是 HTTP 客户端。HTTP 不要求请求背后一定坐着一个人,也不会因为客户端是浏览器就自动给予更多权限。

“服务器”往往是一条处理链

你请求 https://shop.example.test/orders/42,看到的域名像是指向一个服务器。真实链路可能先经过 CDN,再到负载均衡器或反向代理,然后才到应用服务;应用服务还会查询缓存、数据库或其他服务。

从客户端看,它面对的是这个源站所代表的统一接口。至于响应由一台机器直接读取文件,还是由几十个组件协作生成,HTTP 并不强迫客户端知道。

中间节点也不是单纯“转发一下”。代理可能缓存响应、检查身份、压缩内容、记录访问日志、终止 TLS,或者把请求分配给不同实例。这样可以把公共能力从业务代码中拿出来,代价是链路更长,故障和配置问题也更难定位。

客户端、代理、网关与源站的角色链路图

一个页面为什么会触发许多请求

浏览器第一次拿到的通常只是 HTML 文档。解析过程中遇到样式表、脚本、图片和字体地址,它会继续发起请求。脚本执行后,可能再请求用户信息或商品数据。

这些请求不一定都去同一个域名,也不一定都到达源站。有的资源直接命中浏览器缓存,有的由 CDN 返回,有的被 Service Worker 接管。用户看到一个页面,HTTP 看到的却是一组目标不同、缓存策略不同、完成时间也不同的事务。


资源和表示不是一回事

假设你访问“今天的天气”。这个资源显然不是服务器磁盘里永远不变的一个文件。它更像一个可以被标识的概念:某个城市在今天这个时间范围内的天气状态。

HTTP 把请求的目标称为资源。资源可以是一篇文章、一位用户、一个订单集合、一张实时生成的图,也可以是“执行某项操作的入口”。HTTP 不限制资源在服务器内部到底由文件、数据库记录还是程序计算实现。

URI 负责标识目标

资源通常由 URI 标识,Web 开发里最常遇到的是 URL。看下面这个地址:

text
https://api.example.test:443/products/42?currency=CNY#reviews
\___/   \__________________/ \__________/ \__________/ \_____/
方案          权威部分             路径          查询       片段
  • https 表示访问这个资源时要使用受保护的 HTTP 通信。
  • 权威部分通常包括主机名和可选端口,用来确定由谁对这个命名空间负责。
  • 路径与可选查询共同标识该命名空间中的目标资源。
  • 片段 #reviews 通常由客户端在取得表示后自行定位,不会作为 HTTP 请求目标发给服务器。

URL 是标识,不是服务器目录结构的承诺。/products/42 可能映射到数据库查询或远程服务调用,服务器上未必存在 products/42 这个文件。同一个资源也可能拥有多个入口地址,所以不要把“URI”机械理解成资源的唯一数据库主键。

URL 组成与资源定位关系的中文拆解图

表示是资源在某一刻的一种可传输形态

资源本身是抽象目标,HTTP 消息里传输的是资源的表示。同一个商品资源可以有 JSON 表示,也可以有 HTML 表示;同一张图片可以提供 WebP 或 PNG;同一篇内容可以有中文和英文版本。

客户端可以用 Accept、Accept-Language、Accept-Encoding 等字段表达偏好,服务器再选择合适的表示,并用 Content-Type、Content-Language、Content-Encoding 等字段告诉客户端实际返回了什么。这就是内容协商的基本思路。

内容协商减少了 URI 数量,让同一个资源可以服务不同客户端。但它会让缓存键和服务器选择逻辑变复杂。如果响应会随某个请求字段变化,缓存必须知道这个变化条件,否则可能把为甲客户端生成的表示误发给乙客户端。

资源可以变化,表示也可能过期

今天两次请求同一个天气资源,可能得到不同内容。即便资源没有变化,也未必需要每次完整传输。服务器可以给表示附上验证信息;客户端再次请求时带回验证条件,如果表示仍然有效,服务器只需回答“没有变化”,省去正文传输。

这也是理解缓存的关键:缓存保存的通常是某次响应和它携带的表示,不是把抽象资源本身冻结起来。


一次 HTTP 事务如何发生

你点击“查看订单”,页面迟迟没有反应。问题可能出在域名解析、连接建立、TLS 握手、请求排队、服务器处理、响应传输,甚至浏览器拿到响应后的脚本执行。把这些阶段全叫“接口慢”,很难找到真正原因。

课程里可以把一次请求及其对应响应看作一笔 HTTP 事务。这个词帮助我们划定观察单位,但要记住:事务不等于数据库事务,也不承诺原子性、提交或回滚。

从 URL 到请求

以首次访问一个 HTTPS 地址为例,常见过程是:

  1. 客户端解析 URL,确定方案、主机、端口和请求目标。
  2. 如果没有可用缓存,客户端通常通过 DNS 获取可以连接的地址。
  3. 客户端新建或复用连接。HTTP/1.1、HTTP/2 常见的是 TCP;HTTP/3 使用 QUIC。
  4. 需要安全通信时完成加密协商,并验证目标身份。HTTP/3 的安全握手已经集成在 QUIC 中。
  5. 客户端构造请求,写入方法、目标、字段和可选内容。
  6. 请求经过可能存在的代理,最终到达能处理目标资源的服务器。
  7. 服务器解析请求、执行业务逻辑,生成一个或多个响应。
  8. 客户端读取最终响应,根据状态码、字段和内容决定下一步。

这不是每次请求都完整重复的固定流程。DNS 结果可以缓存,连接可以复用,响应也可能直接来自缓存。性能优化常常不是让业务代码“再快两毫秒”,而是减少某个阶段,或者把一次昂贵成本摊到多笔事务上。

HTTPS 请求从 URL 到最终响应的事务时间线

一次请求可能收到不止一个响应

最常见的是一个请求对应一个最终响应,例如 200 OK 或 404 Not Found。不过服务器可以先发送一个或多个 1xx 临时响应,再发送最终响应。临时响应是在告诉客户端“目前进展到这里”,最终响应才结束这次请求的响应序列。

重定向则是另一回事。服务器返回 3xx 最终响应,并在字段里给出新位置;客户端如果决定继续访问,会对新目标发起一笔新的 HTTP 事务。浏览器最后展示一个页面,不代表中间只发生过一次请求。

状态码说明结果,不说明用户界面

状态码是给客户端程序判断结果的三位数字。2xx 表示请求已被成功接收、理解或处理到相应程度,3xx 通常提示还需进一步动作,4xx 指向请求一侧的问题,5xx 表示服务器处理请求时失败。

但状态码不是页面文案。服务器可以返回 404 和一份精心设计的 HTML 错误页,也可能错误地返回 200,正文却写着“订单不存在”。后者会让监控、缓存和客户端逻辑都得到错误信号。后端要让状态码与实际结果一致,再由正文补充业务细节。

浏览器显示“请求失败”时,先区分网络错误和 HTTP 错误。收到 500 说明 HTTP 事务已经到达服务器并拿到了最终响应;DNS 失败、证书校验失败或连接超时,通常根本没有 HTTP 响应。


HTTP 消息怎样表达意图和结果

如果把事务看成一次问答,HTTP 消息就是双方递出的两张纸:请求写“我想做什么”,响应写“结果是什么”。

学习消息结构时,最适合先看可读的 HTTP/1.1。HTTP/2 和 HTTP/3 改变了线上编码和分帧方式,但没有另造一套方法、状态码和资源语义。

请求消息的四个部分

下面是一条带 JSON 内容的 HTTP/1.1 请求:

http
POST /orders HTTP/1.1
Host: api.example.test
Content-Type: application/json; charset=utf-8
Accept: application/json
Content-Length: 31
 
{"productId":42,"quantity":1}

它可以拆成四部分:

  1. 请求行:方法、请求目标和 HTTP 版本。
  2. 请求字段:描述目标主机、内容格式、客户端偏好、认证条件等控制信息与元数据。
  3. 空行:表示字段区结束。
  4. 可选消息体:这里是交给 POST 语义处理的 JSON 内容。

方法不是对 URL 的装饰。它表达客户端希望怎样操作目标资源。GET /orders/42 和 DELETE /orders/42 指向同一目标,却有完全不同的意图、安全性和重试含义。

响应消息也有对应结构

服务器创建订单后可能返回:

http
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: /orders/9001
Content-Length: 42
 
{"id":9001,"status":"pending"}

响应的起始行叫状态行,包括 HTTP 版本、状态码和可选原因短语。后面同样是字段区、空行和可选消息体。

201 已经表达“创建成功”,Location 指出新资源的位置,JSON 正文再给应用需要的订单信息。三者承担不同职责,不能只写一段“success”正文就期待所有客户端猜对结果。

HTTP 请求与响应消息结构对照图

字段是可扩展能力,不是随手塞信息的袋子

HTTP 字段以名称和值传递附加信息。字段名不区分大小写,但不同字段的值有各自语法,不能把它们都当普通字符串拼接。

字段可以影响缓存、条件请求、内容协商、认证、连接管理和跨域行为。它们让 HTTP 能在不改变基本消息模型的前提下扩展。不过,字段越多并不代表设计越好。超大的 Cookie 或重复元数据会增加每次请求的成本,不受约束的自定义字段还会增加代理和客户端之间的兼容负担。

消息体边界必须明确

在持久连接上,接收方读完一个消息后,还要继续读取下一条消息。它必须知道当前消息体在哪里结束。HTTP/1.1 可以根据方法、状态码和字段判断是否存在消息体,再通过明确长度、分块传输编码,或者在部分场景中通过连接关闭确定边界。

这不是无聊的格式细节。如果发送方和中间代理对消息边界理解不同,轻则出现截断或一直等待,重则形成请求走私等安全问题。实际项目应使用成熟 HTTP 库,不要靠字符串切割自行解析线上流量。

HTTP/2 和 HTTP/3 传输的是同一套语义

HTTP/2 把请求和响应映射为二进制帧,并让每组请求与响应运行在各自的流上。常见字段会被压缩,多个流的帧可以交错传输。开发者工具仍会把它还原成方法、目标、字段和状态码,方便人阅读。

HTTP/3 也使用帧和流,只是把承载从 TCP 换成 QUIC,字段压缩机制也相应变化。于是我们可以把 HTTP 分成两层理解:上层是跨版本共享的语义,下层是每个版本如何在连接上传递这些语义。


连接为什么一直在演进

如果一个页面有一百个资源,最直接的实现是为每个资源新建连接、请求一次、收完就关闭。逻辑简单,但网络延迟会不断重复:连接建立要等待,HTTPS 还要协商加密,刚升起来的传输速率又很快被关闭。

HTTP 的版本演进并没有推翻“请求资源、返回响应”这套语义,主要变化之一就是:怎样用更少的等待和更合理的连接承载更多事务。

HTTP/1.0:简单,但经常重复建连

早期 HTTP/1.0 的典型用法是一笔事务使用一条 TCP 连接,响应结束后关闭。实现容易,页面资源一多,连接与握手成本就会迅速累积。

后来一些实现增加了持久连接扩展,但行为并不总是一致。这里适合记住历史背景,不要把今天 HTTP/1.1 的规则套回所有 HTTP/1.0 实现。

HTTP/1.1:默认持久连接

HTTP/1.1 默认让连接保持可复用,除非一方明确表示当前响应后关闭。于是同一个客户端可以在一条连接上依次完成多笔事务,不必每次重新握手。

旧文档经常说“客户端必须发送 Connection: keep-alive 才能复用 HTTP/1.1 连接”,这是把 HTTP/1.0 扩展和 HTTP/1.1 默认行为混在了一起。HTTP/1.1 更常见的关闭信号是 Connection: close。

连接复用节省了建立成本,却没有自动带来理想并发。HTTP/1.1 的流水线允许连续发送多个请求,但响应仍要按请求顺序返回,前面一个慢响应会挡住后面的响应,而且中间设备兼容性曾经很棘手。浏览器因此通常会并行建立若干连接来缓解等待,但连接越多,服务器资源和网络拥塞压力也越大。

HTTP/2:在一条 TCP 连接上复用多条流

HTTP/2 把不同请求和响应放进独立的流,再把各流的帧交错放入同一条 TCP 连接。一个业务响应生成得慢,不再要求其他已就绪响应在 HTTP 消息层面排队等待。头部压缩也减少了大量重复字段的传输。

但“HTTP/2 彻底解决队头阻塞”说得太满。它解决了 HTTP/1.1 的应用层排队问题,却仍建立在一条有序可靠的 TCP 字节流上。如果某个 TCP 数据段丢失,后续字节即使已经到达,也要等缺口补齐后才能交给 HTTP/2;这期间多个 HTTP/2 流都可能被拖住。

HTTP/3:让流在传输层更独立

HTTP/3 把 HTTP 映射到 QUIC。QUIC 运行在 UDP 之上,但它不是“直接使用不可靠 UDP 发网页”。可靠传输、拥塞控制、加密和多路流等能力由 QUIC 自己提供。

QUIC 为每条流分别维护有序字节。某条流的数据丢失时,需要等待的是相关流,其他已经收到完整数据的流可以继续向上交付。QUIC 还把安全握手纳入协议,并通过连接标识支持网络地址变化后的连接迁移,这对移动设备从 Wi-Fi 切到蜂窝网络尤其有用。

代价同样存在。QUIC 和 HTTP/3 的实现、观测与运维更复杂,UDP 在某些网络环境中可能受限,头部压缩仍需谨慎处理跨流依赖。HTTP/3 不是让每个请求无条件变快,而是给高延迟、易丢包和经常切换网络的场景提供更合适的传输基础。

HTTP 1.0、1.1、2 与 3 的连接和并发方式对比

不要用 HTTP 版本替代性能分析。HTTP/2 或 HTTP/3 可能减少连接与排队成本,却不会自动修复慢查询、超大响应、错误缓存策略或拥塞的网络。先找到时间花在哪一层,再决定优化手段。


从一行请求到多路并发

HTTP 的历史不是每隔几年把旧协议全部淘汰,而是在尽量保持上层语义的同时,修补当时最突出的限制。

HTTP/0.9:一次只问一件最简单的事

最早的 HTTP 后来被称为 HTTP/0.9。请求只有一行 GET 加路径,没有版本号、字段和状态码;响应直接返回 HTML 内容。它适合早期的小型超文本系统,但无法清楚表达内容类型、错误结果或缓存条件。

http
GET /index.html

它给后续版本留下了最核心的节奏:客户端指出目标,服务器返回内容。

HTTP/1.0:消息开始能描述自己

HTTP/1.0 在实践中加入版本、方法、状态码和字段。Content-Type 让接收方知道正文是 HTML、图片还是其他格式,HTTP 从“取超文本”走向可以承载多种媒体和应用数据。

它的标准文档更像是对当时通行实现的整理。不同产品曾经先各自尝试功能,再逐渐形成共同规则,这也解释了为什么某些旧式行为今天仍会在兼容代码里出现。

HTTP/1.1:把扩展能力和连接复用做成稳定基础

HTTP/1.1 规范了持久连接、消息长度、缓存控制、条件请求、内容协商和 Host 等能力。Host 让同一个 IP 地址可以为多个站点服务,持久连接减少了页面加载时反复建连的成本。

它经多次修订,今天仍被大量系统使用。所谓“旧版本”不等于已经无用:实现简单、基础设施成熟、逐跳排障直观,都是它继续存在的原因。

HTTP/2:保留语义,重做线上传输

HTTP/2 没有要求应用把所有接口改写一遍。GET 还是读取,404 还是目标未找到。主要改变发生在线上编码:二进制分帧、流多路复用和字段压缩,让一条连接更高效地承载并发事务。

这是一种很有启发的协议演进方式:上层接口尽量稳定,下层表示可以持续优化。浏览器和服务器升级协议,业务路由通常不需要因此重写。

HTTP/3:把多路流交给 QUIC

HTTP/3 延续 HTTP/2 的语义和流式思路,把底层换成 QUIC,以降低 TCP 层队头阻塞的影响,并改善安全握手与连接迁移。

HTTP/1.1、HTTP/2 和 HTTP/3 至今仍会并存。客户端与服务器根据支持情况、配置和网络条件选择协议。部署 HTTP/3 也通常需要保留回退路径,因为不是每条网络都能顺利使用 QUIC。

HTTP 从 0.9 到 3 的能力演进时间线


把这张地图带进后端开发

现在再看一次“打开商品页”这件事:

  1. 浏览器以客户端角色请求某个 URI 标识的资源。
  2. 请求经过连接和可能存在的中间节点到达服务器。
  3. 请求消息用方法、目标、字段和可选内容表达意图。
  4. 后端把这个统一接口映射到路由、权限检查、数据库查询和业务逻辑。
  5. 响应用状态码、字段和可选内容表达处理结果与资源表示。
  6. 连接是否复用、消息如何分帧,由协商出的 HTTP 版本负责。
  7. 浏览器可能根据 HTML 继续请求更多资源,于是新事务接着发生。

遇到问题时也沿着这条链问:目标资源写对了吗?客户端真正发出了什么?中间节点是否改写或缓存?服务器返回的是网络错误还是 HTTP 响应?状态码和正文是否一致?时间消耗在建连、等待处理还是传输?

HTTP 概览的价值就在这里:它不替你直接写出某个接口,却让后面学习 URL、方法、状态码、字段、Cookie 和缓存时,每个概念都有位置可放。下一步,我们会从这张地图里最常被忽略的一块继续往下走——URL 看起来只是一串地址,实际上它已经决定了请求要去哪里、针对什么资源,以及哪些部分根本不会发送给服务器。

下一章URL