普通视图

发现新文章,点击刷新页面。
今天 — 2026年3月10日首页

从一行字到改变世界:HTTP这三十年都经历了什么?

作者 牛奶
2026年3月10日 10:37

这是一个关于「一行字」如何改变世界的故事。从GET /index.html到QUIC,HTTP用了三十年。


原文地址

墨渊书肆/从一行字到改变世界:HTTP这三十年都经历了什么?


1991年,互联网还是个大农村。

那时候上网的人很少,网页也简陋得可怜。你能想象吗——第一个网页上只有一行字,连张图片都没有。

但就是从这一行字开始,一个帝国崛起了。


HTTP/0.9:一切的开始

1991年,一个叫蒂姆·伯纳斯-李(Tim Berners-Lee)的科学家,发明了HTTP

但你绝对想象不到,最初的HTTP能有多简单。

它只有一个方法GET

对,就一个。

你想获取一个网页?好,给服务器发一行字:

GET /index.html

服务器收到,嗷嗷一顿找,然后直接把内容返回给你。就这么粗暴。

没有响应头,没有状态码,没有POST,没有PUT。服务器返回什么,你就看什么。

像什么?

像一个只会说「好」的人。你问一句,它答一句,多余的一个字都没有。

但这就是HTTP的起点——一个简单到不能再简单的协议,奠定了互联网的基石。


HTTP/1.0:第一次进化

1996年,HTTP迎来了第一次大升级。

这时候的互联网已经开始热闹起来了。网页不再是纯文字,图片、音频、视频都冒出来了。原先那套「一行字」的打法,明显不够用了。

时代呼唤改变

人们开始提需求了:

  • 「我想知道请求成没成功」
  • 「我想传输其他类型的文件,不只是HTML」
  • 「我想知道这个页面有没有更新」
  • 「我想把页面做得更好看」

怎么办?HTTP/1.0来了。

新增了什么?

状态码:服务器现在会告诉你结果了。200是成功,404是找不到,500是服务器挂了——就像你问路别人会指路了一样。

请求头和响应头:你可以告诉服务器你能接受什么格式(Accept),服务器也能告诉你返回的是什么类型(Content-Type)、多大(Content-Length)。

支持多种请求方法GET有了,POST也有了。POST可以用来提交表单,比如你填完用户名密码点「登录」。

缓存机制ExpiresLast-Modified这些概念开始出现。浏览器知道什么该存、什么该用了。

这一升级,HTTP从一个只会说「好」的人,变成了一个会「点头摇头」「递纸条」「看备忘录」的完整的人。

但人们还是不满足。


HTTP/1.1:真正的霸主

1997年,HTTP/1.1发布了。

这是一个极其长寿的版本——它统治了互联网整整20多年,直到今天还有很多网站在用它。

你说它有多厉害?

HTTP/1.1的杀手锏

持久连接(Keep-Alive):这是最关键的改动。

以前的HTTP,每次请求都要建立一次TCP连接。请求完就断,断完再连。就像每次说话都要重新握手一样,累不累?

HTTP/1.1说:「别断了,咱们保持连接。」一个TCP连接可以跑完整个页面的所有请求。

管道化(Pipelining):这个就更狠了。

以前是这样的:发请求A,等响应;发请求B,等响应;发请求C,等响应——一个接一个,串着来。

HTTP/1.1可以这样:发请求A、请求B、请求C,一起发出去!不用等A的响应回来再发B。

想象一下你去麦当劳点餐。以前是:「我要一个汉堡」「好」「我要薯条」「好」「我要可乐」「好」。现在是:「我要一个汉堡、一份薯条、一杯可乐」「好嘞」。

爽不爽?

但是!

管道化有个问题:虽然请求一起发了,但响应必须按顺序回来。

这就叫「队头阻塞」(Head-of-Line Blocking)。

就像你点了三份菜,厨房先做了最简单的薯条,但得等你最想吃的汉堡做好了一起上——你只能看着薯条流口水,不能先吃。

这个问题,困扰了HTTP/1.1很多年。

其他小改进

  • 新增了一堆方法PUT(上传)、DELETE(删除)、HEAD(只获取头部)、OPTIONS(查看支持什么方法)
  • 分块传输编码Transfer-Encoding: chunked,服务器可以一块一块地返回数据,不用等全部算完
  • 缓存机制升级Cache-Control登场,比Expires更智能
  • Host头:一台服务器可以托管多个网站了(虚拟主机)

为什么HTTP/1.1能活这么久?

说白了,就是够用

虽然有队头阻塞的问题,但配合CDN、域名分片、静态资源合并这些「野路子」,1.1还是能打的。

再加上升级协议需要服务器、浏览器、CDN厂商全部配合——这是一个生态问题,不是技术问题。

所以HTTP/1.1硬是撑到了2015年,才等来下一代标准。


HTTP/2:真正的革命

2015年,HTTP/2正式发布。

这是HTTP诞生以来最大的一次升级。如果把HTTP/1.x比作绿皮火车,那HTTP/2就是高铁。

发生了什么变化?

二进制分帧:这是最底层的变化。

以前的HTTP/1.x是「文本协议」——你发的请求、服务器回的响应,都是明文写的,像写信一样。

HTTP/2改成了「二进制」——所有数据都转换成0和1来传输,就像发电报。

这意味着什么?效率更高了,因为计算机处理二进制比处理文本快多了。

而且数据被拆成了一个个「帧」(Frame),可以乱序发送、并行接收,彻底解决了队头阻塞!

多路复用(Multiplexing):这是HTTP/2的核心杀手锏。

一个TCP连接里,可以同时跑多个「流」(Stream)。每个流里可以双向传输数据,帧可以乱序、可以交叉。

还是点餐的例子:以前是点三份菜等服务员一份一份上,现在是服务员端着一个大盘子,三份菜一起给你端上来,而且你还可以边吃边点。

爽到飞起。

服务器推送(Server Push):这个功能也很革命。

以前是这样的:浏览器请求页面HTML → 服务器返回 → 浏览器解析HTML发现要CSS → 再请求CSS → 服务器返回 → 浏览器解析CSS发现要图片……

一套下来,浏览器累得够呛。

HTTP/2说:「别麻烦了,我知道你要什么。」服务器在返回HTML的时候,直接把CSS、图片一起推给你。

就像你去饭店,服务员看你落座就把碗筷、茶水、菜单一起摆好了——不用你开口。

头部压缩(HPACK)HTTP/1.x每次请求都要带一堆header,很多还是重复的。HTTP/2用HPACK算法压缩header,能省70%以上的流量。

HTTP/2的遗憾

HTTP/2解决了应用层的队头阻塞,但TCP层面还有个问题——丢包。

HTTP/2所有请求都跑在一个TCP连接上。如果其中一个包丢了,整个连接都要等它重传成功。

怎么办?

换协议呗。


HTTP/3:UDP登场

2018年,HTTP/3正式发布。

这是HTTP第一次抛弃TCP,拥抱UDP

为什么要用UDP?

TCP太可靠了——它保证数据一定到达、按顺序到达、中途不能出错。

但有时候,我们不需要这么可靠。

比如看直播——丢了一帧画面有什么关系?下一帧就来了。我要的是,不是「绝对不出错」。

UDP就是这样的「愣头青」——我负责发,你负责收,收没收到、有没有乱序,我不管。

这反而成了优势。

QUIC:HTTP/3的核心

HTTP/3用的是QUIC协议(Quick UDP Internet Connections),它是Google发明的,后来被IETF收编。

QUICUDP的方式,实现了TCP的效果:

  • 无队头阻塞:每个「流」是独立的,一个流丢包不影响其他流
  • 0-RTT连接:第一次连接后,后续连接可以瞬间建立(省去握手时间)
  • 连接迁移:你从WiFi切到5G,IP地址变了,连接不会断——因为QUIC用的是连接ID,不是IP地址
  • 内置TLS:TLS握手和QUIC握手一起完成,又省了一轮时间

简单说:TCP的优点我都有,TCP的缺点我避开,我还比TCP快。

HTTP/3带来了什么?

  • 更低的延迟
  • 更好的移动端体验(WiFi/5G切换不断连)
  • 抗丢包能力更强

HTTP/3也有问题:它需要服务器和客户端都支持,而且UDP在某些网络环境下可能被限速或被墙。

所以HTTP/2HTTP/3现在在并存使用,未来可能会慢慢过渡到3。


结尾:从一行字开始,到改变世界

回顾HTTP的进化史,你会发现一条清晰的线:

版本 年份 核心特点
HTTP/0.9 1991 只有一个GET,一行字
HTTP/1.0 1996 状态码、请求头、POST、缓存
HTTP/1.1 1997 持久连接、管道化、虚拟主机
HTTP/2 2015 二进制分帧、多路复用、服务器推送
HTTP/3 2018 QUIC + UDP、0-RTT

一行字到改变世界,从文本到二进制,从TCPUDP——HTTP用了三十多年。

今天,你打开任何一个网站、刷任何一个App、点任何一个链接——背后都是这一行字的子孙后代在为你工作。

这就是技术的魅力:从一个简单的想法出发,最终改变了整个世界。

浏览器到底在偷偷帮你做什么?——HTTP缓存与刷新机制

作者 牛奶
2026年3月10日 08:49

你的浏览器可能比你想象中更"勤俭持家"。这篇文章聊聊它是怎么帮你省流量、省时间的,以及什么时候该阻止它。


你有没有注意过这样一个现象?

第一次打开一个网页,要等好几秒。第二次打开同一个网页,基本秒开。

你是不是以为浏览器記住了这个页面?它到底是怎么做到的?

今天我们来聊聊浏览器缓存——这个你天天在用,却可能从来没认真了解过的机制。


原文地址

墨渊书肆/浏览器到底在偷偷帮你做什么?——HTTP缓存与刷新机制


缓存到底是个什么东西?

想象一下这个场景。

你每天早上都去楼下的早餐店买豆浆。第一次去的时候,老板娘要问你「要甜的還是甜的」「要不要加鸡蛋」「打包还是在这吃」——问半天,你回答半天。

但如果你连着去了一个星期,每天都点「原味豆浆,不加糖,带走」。

第七天的时候,你刚走到门口,老板娘已经把豆浆打包好了递给你,连话都不用说一句。

这就是缓存。

浏览器做的就是这件事。第一次访问一个网页,它要千里迢迢去服务器「要」资源。第二次访问,它就聪明了——直接从自己的「小仓库」里拿出来给你用,省时又省力。

但问题来了:浏览器怎么知道哪些东西可以直接用?哪些东西已经过期了?

这就涉及到HTTP缓存的两个核心概念:强缓存协商缓存


强缓存:先用了再说

强缓存就像是你跟浏览器达成的一个小协议。

你告诉它:「这个资源在未来一年内都不会变,你就放心大胆地用,别来问我。」

浏览器听了这话,就直接把资源扔给你,连服务器的大门都不带敲的。

那这个"一年"的承诺是怎么实现的呢?

Expires:看时间

最早的方式是用Expires这个响应头:

Expires: Wed, 21 Oct 2026 07:28:00 GMT

这就像在商品上贴了个过期日期:「2026年10月21日之前都有效。」

但这个方式有个bug:如果你的电脑时间和服务器时间不一致,比如你把电脑调快了一个月,那缓存就直接失效了——浏览器会以为已经过期了,实际上还能用。

Cache-Control:更聪明的做法

后来HTTP/1.1推出了Cache-Control,就像给缓存装了个更智能的计时器:

Cache-Control: max-age=3600, public

max-age=3600的意思是:缓存有效期是3600秒,也就是一个小时。

这个比Expires靠谱多了——它是相对时间,不受系统时间影响。

而且Cache-Control还支持一堆指令,让你能更精细地控制缓存行为:

指令 意思 什么时候用
public 缓存可以放在CDN、代理服务器上 静态资源
private 只准浏览器自己缓存 个性化内容
no-cache 每次用之前先问问我 经常变化的资源
no-store 别存了,每次都重新拿 敏感数据
immutable 这东西永远不会变 版本化后的静态资源

这里有个重点:no-cache不是"不缓存",而是"用之前先问一下"。


协商缓存:问一下再决定

强缓存虽然快,但有个问题:如果服务器上的资源已经变了,浏览器可不知道。

你跟浏览器说「这个文件缓存一年」,结果三个月后你更新了网站,浏览器还在用旧的——用户就看不到新内容了。

怎么办?

协商缓存就是这个问题的答案。

它的逻辑是这样的:浏览器还是要去问一下服务器:「我这里有个缓存,还能用吗?」

服务器说「能用」(资源没变),返回304 Not Modified,浏览器继续用缓存。

服务器说「不能用」(资源变了),返回200 OK + 新资源,浏览器更新缓存。

Last-Modified:看修改时间

最早的方式是用Last-Modified

# 服务器返回
Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

# 浏览器下次请求
If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT

就像你问老板:「这个文件最后改过是什么时候?如果还是那天下午,我就继续用我的版本。」

但这个方式有个缺点:精度只到秒。如果一秒内文件被修改了两次,浏览器可看不出来。

ETag:看"指纹"

后来就有了ETag,它给每个文件生成一个"指纹":

# 服务器返回
ETag: "abc123"

# 浏览器下次请求
If-None-Match: "abc123"

这个指纹通常是文件的MD5哈希值,或者其他能唯一标识内容的东西。

服务器收到请求后,会比较一下ETag:如果指纹一样,说明内容没变, 返回304;如果不一样,返回200 + 新内容。

划重点:ETag的优先级高于Last-Modified。如果服务器同时给了这两个,浏览器会先用ETag。


缓存到底是怎么工作的?

让我给你串起来整个流程:

你访问一个页面
    ↓
浏览器先看强缓存(Cache-Control / Expires)
    ↓
[命中了] → 直接从本地拿 → 页面秒开 ✅
    ↓ [没命中]
浏览器带着缓存标识去问服务器
    ↓
服务器比较ETag / Last-Modified
    ↓
[资源没变] → 返回304 → 浏览器用缓存 → 依然很快 ⚡
    ↓ [资源变了]
服务器返回200 + 新资源 + 新标识
    ↓
浏览器更新缓存,展示新内容

这个流程你理解之后,很多奇怪的现象就能解释了:

  • 为什么更新了CSS但页面没变?——可能被强缓存了
  • 为什么F5刷新后还是旧内容?——正常,优先用强缓存
  • 为什么Ctrl+F5就变了?——强制刷新,绕过了所有缓存

刷新这件事,讲究这么多?

说到刷新,我发现很多人(包括以前的我)对浏览器的刷新按钮有误解。

你以为的刷新:「重新加载这个页面」

实际上的刷新:「emmm,让我先看看缓存有没有过期」

正常刷新(F5 / 点击链接)

这是最常用的方式。

浏览器会先看强缓存,过期了吗?没有就直接用。过期了就去协商,服务器说能用就用。

结果:可能快(304),也可能慢(200),取决于缓存状态。

强制刷新(Ctrl+Shift+R / Ctrl+F5)

这个才是真正的"重新加载"。

浏览器会跟服务器说:「少废话,把所有资源都给我拿新的,别跟我提缓存!」

请求头里会带上Cache-Control: no-cache,或者直接忽略所有的缓存标识。

结果:总是200,慢但保证最新。

开发者工具里的选项

Chrome DevTools里其实有更精细的控制:

  • 禁用缓存:只有DevTools打开时才生效,模拟no-cache
  • 硬性重新加载:等于强制刷新
  • 空缓存并硬性重新加载:先清空所有缓存,再强制刷新——相当于把浏览器的小仓库一把火烧了

说完了缓存,来聊聊安全

缓存虽好,但有时候也会带来安全问题。

比如你登录了一个网站,浏览器缓存了你的个人信息。然后别人用了你的电脑,打开同一个网站——诶,怎么直接就登录了?

这就是缓存的"副作用"。所以有些东西是不能缓存的。

no-store:敏感数据别存

Cache-Control: no-store

这个指令告诉浏览器:「看归看,但别存到硬盘上。」内存里用完就扔,下次重启就没了。

登录后的用户信息、token、支付相关的数据——这些都应该加上no-store

private:只存浏览器

Cache-Control: private

这个的意思是:「可以缓存,但只能存在用户自己的浏览器里,别给CDN、代理那些中间商。」

用户信息存private,静态资源存public——这是基本的安全常识。


聊完缓存,再聊聊HTTPS

既然说到安全,顺便提一下HTTPS。

你可能知道HTTPS是"加密的HTTP",但它跟缓存有什么关系呢?

其实没有直接关系。但HTTPS有一个相关的安全头:

Strict-Transport-Security: max-age=31536000; includeSubDomains

这叫HSTS,简单说就是告诉浏览器:「以后别用HTTP跟我玩了,直接给我用HTTPS。」

防止一种叫"SSL剥离"的攻击——黑客在中间把HTTPS降级成HTTP,偷看你的数据。

还有哪些安全头值得关注?

作用
X-Frame-Options: DENY 防止别人把你的页面嵌在iframe里(点击劫持)
X-Content-Type-Options: nosniff 告诉浏览器别乱猜内容类型
Content-Security-Policy 限制资源加载来源,防止XSS
Referrer-Policy 控制 Referrer 信息泄露

这些安全头配置起来不麻烦,但能挡住很多常见攻击。建议都配置上。


到底该怎么用?

说了一堆原理,最后来点实用的。

不同资源,不同策略

资源类型 推荐策略
JS / CSS / 图片 max-age=31536000, immutable(一年,不用改)
HTML页面 no-cache,每次都协商
API接口 no-cacheprivate
用户敏感数据 no-store

核心原则

  • 静态资源:狠命缓存,文件名带hash,变了就改文件名
  • HTML:别缓存,随时要最新
  • API:看场景,频繁变化的要协商,不变的可以强缓存
  • 敏感数据:有多远滚多远,别缓存

总结一下

这篇文章聊了浏览器缓存和刷新机制的核心概念:

  • 强缓存Cache-Control / Expires):直接用,不过问服务器
  • 协商缓存ETag / Last-Modified):问一下服务器能不能用
  • 刷新:F5看缓存,Ctrl+F5硬刷新
  • 安全no-store防敏感数据泄露,private防中间商缓存
  • HTTPS相关:HSTS、安全响应头

理解这些机制之后,你就能解释很多奇怪的现象,也能更好地优化你的Web应用性能。

❌
❌