你有没有过这种经历:朋友发来一个链接,点开之后屏幕一片白,转圈转得人心烦。关掉再点一次,唰地就出来了。不是你的网突然变好,而是第一次访问和第二次访问,浏览器和服务器要干的活完全不一样。
第一次打开一个网站,浏览器对它是完全陌生的。它不知道这个域名对应哪台服务器,不知道服务器支不支持HTTPS,不知道页面里有哪些图片和脚本,更不知道这些东西能不能重复用。所有事情都得从头来一遍。
最开始的环节是DNS解析。你输入网址,浏览器得先问DNS服务器:这个域名对应的IP地址是多少?如果本地没有缓存,这个询问可能要经过好几层递归,从根域名服务器一路问到权威服务器。国内普通宽带下,一次冷DNS解析通常要50到200毫秒。如果用了不太靠谱的公共DNS,或者域名解析记录多、TTL短,这个时间还会更长。你感觉到的“卡了一下”,很可能就是这里。
拿到IP之后,浏览器要和服务器建立TCP连接。TCP三次握手,简单说就是客户端发个SYN,服务器回SYN+ACK,客户端再回ACK。这一来一回至少消耗一个网络往返时间,也就是RTT。如果服务器在国内,RTT可能20到50毫秒;如果服务器在美国,RTT轻松超过150毫秒,甚至300毫秒。一个握手就够你等半秒。
如果是HTTPS网站,TCP连上之后还要做TLS握手。TLS 1.2需要两个RTT,TLS 1.3优化到一个RTT,但依然要交换密钥、验证证书。证书链如果没配好,或者中间证书缺失,浏览器还要额外去下载,那就更慢。有测试显示,在移动网络下,TLS握手平均能占到首次加载总时间的15%到20%。

连接建好了,浏览器终于发出第一个HTTP请求。服务器收到请求后,也不是立刻就能把页面吐出来。如果这个页面是动态生成的,比如电商首页、新闻详情页,服务器可能要去数据库查数据、跑模板渲染、调用其他接口。第一次访问时,服务器端的缓存往往是空的,所有计算都得实打实做一遍。一个没做缓存的动态页面,服务器处理时间超过500毫秒很常见,复杂查询甚至要两三秒。
服务器返回HTML后,浏览器开始解析。它发现页面里引用了CSS、JavaScript、图片、字体。这些东西第一次访问时,本地缓存里一个都没有。浏览器得挨个去下载。根据HTTP Archive的统计,现在一个普通网页的中位数大小在2.3MB左右,请求数大约75个。这里面图片占大头,JavaScript和CSS也不少。如果服务器没开压缩,或者图片没优化,文件更大。浏览器对同一个域名的并发连接数有限制,一般6个左右,剩下的请求就得排队。排队加上下载,时间就上去了。
更麻烦的是,JavaScript默认会阻塞HTML解析。浏览器遇到一个没有async或defer的script标签,必须停下来,下载、执行完,才能继续往下解析。第一次访问时,这个JS文件没缓存,下载要时间,执行也要时间。如果脚本里还有同步的XHR请求,那就更卡。字体文件也一样,如果用了自定义字体,浏览器要等字体下载完才能显示文字,否则就是空白或者闪烁。
服务器那边也有缓存策略。很多网站用了CDN,但CDN节点第一次被访问时,缓存是空的。它得回源站去取内容,取回来再缓存。你作为第一个访问这个节点的用户,就承担了回源的时间。后面的人再访问,CDN直接返回,就快多了。这就是为什么有些网站第一次打开慢,刷新一下或者过几分钟再打开,速度完全不一样。
第二次打开快,是因为前面这些步骤大部分都被省掉了。DNS有本地缓存,不用再问。TCP连接可能被复用,或者至少握手更快。TLS会话可以复用,省掉密钥交换。CSS、JS、图片都在浏览器缓存里,直接读本地。服务器端和CDN也有缓存,不用重新计算。所有环节都从“冷”变成了“热”,自然就快了。
Google做过研究,移动端页面加载超过3秒,53%的用户会离开。亚马逊也发现,页面加载每慢100毫秒,销售额下降1%。这些数字说明,第一次打开慢不是小问题,它直接影响用户会不会留下来。
想改善第一次打开的速度,对普通用户来说,可以试试换个更快的DNS,比如运营商默认DNS或者阿里、腾讯的公共DNS,有时候能省几十毫秒。对网站开发者来说,能做的事情更多:把静态资源放CDN,开启HTTP/2或HTTP/3,启用TLS 1.3,压缩图片和文本,给动态页面加服务端缓存,用preconnect和dns-prefetch提前建立连接,把关键CSS内联,JS用defer加载。这些手段加起来,能把首次加载时间砍掉一大半。
说到底,网站第一次打开慢,不是某一个原因造成的,而是DNS、TCP、TLS、服务器计算、资源下载、浏览器渲染这一整条链路上,每个环节都在“从零开始”。第二次快,是因为前面已经铺好了路。理解了这一点,你就不会觉得是网络在故意跟你作对了。