HTTP(HyperText Transfer Protocol,超文本传输协议)就是浏览器和 Web 服务器之间的对话规矩:一问一答。浏览器递上一张请求报文(三部分:起始行 / 头部 / 正文),服务器回一张响应报文(结构一样,起始行换成状态行)。两张纸的规矩完全相同:头部一行一个"名字: 值",然后一个空行,之后才是正文。这场对话有三条脾气要记住:服务器永远不主动开口;服务器不记得你是谁(这就是"无状态",也解释了为什么需要 Cookie);而响应开头那三位数字(状态码),是全世界排障最快的暗号——看第一位就知道该怪谁。
4.1 看见的是"网站由四件套构成、门房守着 80/443";4.2 与 4.3 拆开了"内容文件"本身;4.4 把地址讲透,并且在结尾留下了一个悬念——URL 的六段里,只有"路径 + 查询串"装进请求行发出去。
本节就从那行请求行往下写:它后面还跟着什么、服务器怎么回、那些字头各自管什么、状态码怎么看。
一、HTTP 是什么
HTTP = HyperText Transfer Protocol = 超文本传输协议。 逐词拆:
| 词 | 意思 | 说人话 |
|---|---|---|
| HyperText(超文本) | 带链接的文本 | 就是 4.2 那个 HTML——能点着跳来跳去的文本 |
| Transfer(传输) | 搬运 | 把网页从服务器搬到浏览器 |
| Protocol(协议) | 规矩 | 双方约定好的说话格式(收 1.6"协议=通信语言") |
它在应用层(收 1.6 的四层地图)。脚下踩着两双鞋: - TCP 负责可靠地把字节送到(收 3.18:分段、编号、丢了重发) - IP 负责找路(收 3.7)
所以 HTTP 自己完全不用担心"路上会不会丢"——它只负责把规矩写好。这就是 1.6 "分层"最实际的好处:每层只管自己那件事。
curl 在用它、手机 App 在用它、后台服务之间也在用它;传的不只是网页,还有 图片、JSON 数据、上传的文件。
判断依据是内容里写明了是什么(Content-Type),而不是"它长得像不像网页"。
版本简史(知道差别就够):
| 版本 | 关键变化 | 解决什么 |
|---|---|---|
| HTTP/1.0 | 一问一答,问完就断线 | 最早的规矩 |
| HTTP/1.1 | 持久连接(一条连接上问好几轮)+ 必须带 Host 头 |
不用为每个文件重新握手;一台服务器能挂多个网站 |
| HTTP/2 | 一条连接上并行跑多路 + 头部压缩 | 页面资源多时更快 |
| HTTP/3 | 改跑在 UDP 上(收 3.20) | 弱网下更抗丢包 |
网页要几十上百次请求,为什么还不太慢?因为 HTTP/1.1 的持久连接——那几十次都跑在同一条已经建好的连接上,不必每个文件都重新握手。
二、一问一答:请求报文
浏览器发出去的那张纸,只有三部分(配图一左):
① 起始行(请求行)——一行讲完全部要点:
GET /index.html HTTP/1.1
│ │ └── 用哪个版本的规矩说话
│ └───────────── 要的东西(只有路径,没有域名!)
└────────────────────── 方法:我要做什么
- 方法:
GET(把东西给我)/POST(带着数据去提交)/HEAD(只要头部、不要正文)/ 还有PUT、DELETE、OPTIONS - 为什么没有域名? 因为域名已经在 4.4 说的那个
Host头里了,不然一台服务器上挂的几个网站分不清你在找谁(收 4.1)
② 头部字段——一行一个「名字: 值」,是这场对话里所有"附加说明"的家:
| 请求头 | 说的是什么 | 说人话 |
|---|---|---|
Host |
要哪个站 | HTTP/1.1 必须带,一台服务器多网站全靠它分流 |
User-Agent |
我是谁 | 浏览器类型与版本,网站靠它做兼容 |
Accept / Accept-Language |
我能看懂什么、要哪种语言 | 会不会给你中文页,看它 |
Referer |
我从哪个页面点过来的 | 统计来源(也涉及隐私) |
Cookie |
我的身份凭证 | 下一节的主角 |
Connection |
这条连接怎么用(如 keep-alive) |
别问完就断,留着继续用 |
③ 空行 + 正文——那个空行是硬规定,服务器靠它知道"头部到此为止"。
GET请求通常没有正文(你要的是东西,没东西要递)POST请求有正文:表单里填的内容、上传的文件,都在这里
三、一问一答:响应报文
服务器回的那张纸,结构一模一样,只是把第一行换了名字(配图一右):
① 状态行:HTTP/1.1 200 OK —— 版本 + 三位数字 + 一句人话
② 响应头——这里的几个关键角色:
| 响应头 | 说的是什么 | 为什么重要 |
|---|---|---|
Content-Type |
正文是什么内容 | 浏览器靠它决定怎么显示——是网页、图片还是 JSON。⚠️ 不是靠文件后缀(4.2 说过的"后缀无所谓"就是这个道理) |
Content-Length |
正文多少字节 | 对面知道该收多少才停(收 3.18 分段与长度) |
Cache-Control / ETag / Last-Modified |
这份东西能缓存多久、怎么判断有没有变 | 决定你下次打开快不快(收 3.21 缓存的老思路) |
Set-Cookie |
给你一张身份证 | 下一节的引子 |
Server |
门房是谁(nginx / Apache / IIS) | 4.1 里 curl -I 认门房看的就是它 |
③ 空行 + 正文:正文就是你要的东西本身——HTML 文本、图片的二进制、JSON 数据……这才轮到浏览器去看内容。
一张报文里,头部字段全是给浏览器、缓存、代理这些程序读的"说明标签";正文才是最终呈现在你眼前的东西。
这也是一句老话的来源:"东西明明下载下来了,打不开是因为类型不对"——正文拿到了,但 Content-Type 说不认识。
四、状态码:三位数字里的一整套语言
状态行的第一个重点就是那三位数字。第一位就决定了它属于哪一族(配图二),剩下的两位是族内的具体编号。
| 族 | 含义 | 谁的问题 | 你该干什么 |
|---|---|---|---|
| 1xx | 信息,还在处理 | 都不是 | 极少见,不用管 |
| 2xx | 成功 | 都不是 | 正常 |
| 3xx | 重定向,去新地址 | 都不是 | 浏览器会自动跟着跳 |
| 4xx | 客户端出错 | 你这边的请求有问题 | 先查自己:地址写错?没登录?没权限? |
| 5xx | 服务器出错 | 服务器那边有问题 | 你改也没用,等一等或找运维 |
最常撞见的几个,逐个认脸:
- 200 OK:一切正常。但注意,200 只代表"请求被正确处理了",不代表内容是你要的——404 页面本身也可以配成返回 200。
- 301 永久搬家 / 302 临时搬家:301 会让浏览器记住新地址,下次直接去新家;302 只是"这次先这样,下次还得问"。4.4 里"末尾斜杠被区别对待"、
example.com跳www.example.com,走的就是这类跳转。 - 304 Not Modified:不是错误。意思是"你手里那份还能用,别下了"。这是缓存机制的省流量担当——浏览器把上次的副本和标记(
ETag/Last-Modified)一起问,服务器一看没变,就只回一个 304,正文都不发(收 3.21 缓存)。 - 404 Not Found:东西不在。多半是地址写错、或者资源被删了。
- 403 没权限 / 401 没登录:401 是"你得先证明身份",403 是"身份我知道了,但你不许进"。
- 500 内部错误 / 503 忙不过来:服务器自己出问题。这类常常是暂时的,所以"刷新一下"经常真有用。
4 开头先查自己,5 开头先问服务器。
这一条能省下大量"是不是我网络坏了"的无效怀疑。
五、HTTP 是"无状态"的
这是 HTTP 最重要的性格:每一个请求都是独立的一张纸,服务器不会记得上一个请求。
因为它简单、好扩展:任何一台服务器收到任何一个请求,只看这张纸就能干活,不需要"知道之前发生过什么"。
这样一来,一台忙不过来可以再加一台(收 1.7 的弹性伸缩),你这次请求打到哪台机器上都一样。
代价也很具体:登录、购物车、多步表单这些"要记住"的事,HTTP 本身做不到。于是有了补丁——Cookie:
- 第一次你去,服务器在响应头
Set-Cookie里塞给你一张小纸条 - 之后每次请求,浏览器在请求头
Cookie里自动带上这张纸条 - 服务器一看纸条:哦,是老熟人
一张纸条把"无状态"的对话接成了"有记忆"的对话。这就是为什么清掉 Cookie 之后,很多网站会把你当成陌生人。
六、一次页面加载,其实是好几轮对话
把 4.1 的账本和本节合起来看:一次打开网页 = 几十上百次请求 = 几十上百轮一问一答,每一轮都各自有一对完整的报文。
顺序也很讲究(收 4.2):
- 先要 HTML(1 次)
- 浏览器边读边发现:"还要一个 CSS、两个 JS、三张图"
- 于是再各要一次(每个都是一轮完整的请求-响应)
所以"页面一点点长出来"其实很直观:HTML 先到,其余的东西一轮一轮补进来。
七、动手 Demo(Demo42)
目标:把 F12 里那一堆"标头",和本节图一的三段结构对上号。
第 1 步 · 用 curl -v 看报纸原件(最直观的一步)
curl -v https://example.com
找两批行:> 开头的是你发出去的请求、< 开头的是服务器回的响应。对照图一,一行一行认。
第 2 步 · 只要头部:curl -I
curl -I https://example.com
这就是 HEAD 方法(只取头部不要正文),输出里能看到 Server(认门房)、Content-Type(是什么内容)、Cache-Control(能缓存多久)。
第 3 步 · 在 F12 里对上图一的左右两半 F12 → 网络 → 随便点一条请求 → 「标头」里分成请求标头和响应标头两堆。把图一那两个面板摆在旁边对,一次就记住了。
第 4 步 · 亲手改一次方法
curl -X POST -d "name=bozai" https://httpbin.org/post
看回显里 "form" 那一栏——你 POST 的正文被服务器读出来了。这也证明:GET 要东西、POST 递东西。
第 5 步 · 故意撞 404 和 301
- 访问一个不存在的路径(如 curl -I https://example.com/nope)→ 看状态行变成 404
- 用 curl -I http://example.com(不带 www)→ 常能看到 301 和一个 Location: 头——这就是"搬家了去新地址"的实物
第 6 步 · 抓一次 304(缓存在干活) 刷新页面,然后再刷新一次,在网络面板里看静态资源那条:状态会变成 304 或"来自缓存",大小几乎为 0。 这就是"第二次打开更快"的机制(收 3.21 缓存)。
第 7 步 · 看 Cookie 一进一出
F12 → 应用 → Cookie 里看有哪些;再回网络面板,找响应头里的 Set-Cookie 和请求头里的 Cookie——一张纸条怎么发出去、怎么带回来,全在眼前。
第 8 步 · 用 4.2 那个门房日志再品一次
python -m http.server 8000
刷新页面,看窗口里一行行刷出来的记录——每刷一行,就是一轮刚刚完成的 HTTP 对话。
八、30 秒实验
- 30 秒:
curl -v https://example.com之后,数一数>有几行、<有几行——图一是示意,这才是真货。 - 30 秒:
curl -I https://example.com/不存在的页面,看状态行从 200 变成 404。 - 30 秒:在 F12 里随便点一条请求,只看
Content-Type——是text/html、image/png还是application/json?看三个你就明白"内容类型"这回事了。
九、常见误区
- "HTTP 就是浏览器用的、就是网页。" —— 它是通用的对话规矩,
curl、App、后台服务都在用;传的也不只是网页。 - "请求行里怎么没有域名?是不是我看漏了。" —— 域名在
Host头里(收 4.4)。请求行只有方法 + 路径 + 版本。 - "404 就是网站挂了。" —— 404 是东西不在(多半地址写错)。网站挂了更像 5xx 或干脆连不上。
- "返回 200 就说明没问题。" —— 200 只说"请求被正确处理了"。内容对不对,还得看正文。
- "文件类型看后缀不就行了。" —— 浏览器看的是响应头
Content-Type,不是后缀(4.2 已埋过这条)。 - "一次请求就是一个页面。" —— 一次页面加载是几十上百轮对话(收 4.1)。
- "HTTP 记得我上次来过。" —— 无状态。记得你的是 Cookie,不是 HTTP。
- "304 是个错误。" —— 304 是好消息:你手里的缓存还能用,省下一次下载。
- "头部字段随便写、不写也无所谓。" —— HTTP/1.1 里
Host必须带,Content-Type不带浏览器就可能不认。 - "HTTPS 是另一套协议,报文结构不一样。" —— HTTPS 就是 HTTP 外面套了一层加密,报文内容一点没变(4.4 说过:那是"穿不穿衣服"的区别)。
十、动手自测
- HTTP 三个字母是什么?它在哪一层?下面踩着谁?
- 请求报文和响应报文各由哪几部分组成?那个"空行"是干什么用的?
- 请求行里有哪三样东西?为什么没有域名?
GET和POST最本质的区别是什么?- 哪个响应头决定浏览器把正文当网页还是当图片?它和后缀是什么关系?
- 状态码有哪五族?各是谁的问题?
- 301 和 302 差在哪?304 又是什么?
- "HTTP 是无状态的"是什么意思?那登录状态靠什么维持?
- 一次页面加载为什么是"几十上百轮对话"?这些对话是怎么排队的?
- HTTPS 和 HTTP 的报文结构一样吗?区别在哪?
答案:
① HyperText Transfer Protocol(超文本传输协议),在应用层;下面踩着 TCP(可靠送到)和 IP(找路)——所以 HTTP 自己不管会不会丢。
② 都是三部分:起始行 + 头部字段 + 空行 + 正文(响应报文的起始行叫状态行)。空行是硬规定,用来告诉对方"头部到此为止,后面是正文"。
③ 方法 + 路径 + 版本(如 GET /index.html HTTP/1.1)。没有域名,因为域名在 Host 头里——一台服务器挂多个网站,得靠它分流(收 4.4)。
④ GET 是"把东西给我"(要),POST 是"带着数据去提交"(给)。所以 GET 一般没有正文,POST 有正文。
⑤ Content-Type。浏览器只看它,跟文件后缀无关(4.2 那条"后缀无所谓"就是从这来的)。
⑥ 1xx 信息 / 2xx 成功 / 3xx 重定向 / 4xx 客户端出错 / 5xx 服务器出错;4 开头先查自己,5 开头先问服务器。
⑦ 301 永久搬家(浏览器会记住新地址,下次直接去)、302 临时(每次都还得问一下);304 不是错误,是"你手里那份还能用,别下了"——缓存在省的流量。
⑧ 意思是每个请求都是独立的一张纸,服务器不记得上一个请求(换来的是简单、好扩展、能随意加机器)。登录状态靠 Cookie:服务器用 Set-Cookie 发一张纸条,浏览器之后每次请求都自动带回去。
⑨ 因为每个 CSS、JS、图片都要各要一次(收 4.2),而且先拿到 HTML 才知道还要什么——所以只能一轮一轮来。它们大多跑在同一条持久连接上(HTTP/1.1 的功劳),所以还不太慢。
⑩ 完全一样。HTTPS 只是把整条 HTTP 报文套进加密通道里(4.4 说的"穿不穿衣服"),报文的三段结构、头部、状态码都没变。
十一、下一步学什么
- ☑ 传输Web网站的文件(HTTP响应) —— 本节把“一次对话”讲完了(并留下两处尾巴:Cookie 那张纸条、网页里那些"动起来"的部分)。这就换个位置,站到服务器那一侧看它把文件装进响应前要做的功课:定类型、定长度、压不压、给不给整份、附不附版本标记——顺带把"304 是怎么发生的"这笔账彻底结清。
相关笔记
- Web网站的地址(URL) —— 上一节:URL 六段里只有"路径 + 查询串"进请求行,本节接着从这行往下写
- Web网站是如何构建的(Web网站) —— 门房守着 80/443、
curl -I看Server头、以及"一次页面几十上百次请求"这本账 - 制作Web网页(HTML) —— 每个
<img>/<link>都是一次请求;"文件后缀无所谓"的答案就在Content-Type里 - 设计Web网页的外观(样式表) —— CSS 是"多一次请求但能被缓存",本节第 6 步那个 304 就是它在省流量
- 对应用程序的数据进行可靠传输(TCP) —— HTTP 不担心丢失,正是因为底下有 TCP 在兜
- 只对传输给应用程序的数据进行分发(UDP) —— HTTP/3 改嫁到了 UDP 上
- 根据端口号分配数据到应用程序(端口号、已知端口号) —— 80 / 443 这两个端口后面站着的,就是收发这些报文的门房
- 网络的电话簿(域名解析、主机名、DNS) —— 缓存的思路一脉相承:DNS 有 TTL,HTTP 有
Cache-Control与 304 - 通信过程中所使用的语言(网络架构) —— 应用层这一格的主体内容:协议=通信语言,HTTP 就是其中最忙的那门语言
- 运行和管理服务器(服务器、云服务、内部部署) —— "无状态"为什么值得:任何一台机器都能接你的下一个请求,才好加上机器