浏览器实际上如何与服务器通信(无废话指南)
Source: Dev.to
我多年使用网络,却并未真正了解它的工作原理。我可以构建应用并发布功能,但如果有人问“当你输入 URL 并按回车时到底发生了什么?”我只能给出一个半对的答案,然后希望话题继续下去。
这篇文章就是我早该拥有的解释。没有历史课,没有协议琐事——只讲浏览器和服务器到底是如何通信的。
第一步:浏览器不知道你的服务器
当你输入
https://collabspace.app
你的浏览器根本不知道该服务器在哪里,于是它会查询 DNS:
“嘿,哪个 IP 地址对应
collabspace.app?”
DNS 会返回类似 203.0.113.42 的地址。就这么简单——只是一场电话簿查询。如果 DNS 速度慢或出现故障,即使后端完美,你的应用也会感觉很慢。
第 2 步:在 HTTP 之前先建立 TCP(此部分很重要)
在任何 HTTP 请求出现之前,浏览器会:
- 打开到
203.0.113.42的 TCP 连接。 - 如果 URL 为 HTTPS,则协商加密(TLS)。
此握手会耗费时间,这就是冷启动感觉慢的原因。它也解释了为何会有 HTTP/2 和 HTTP/3,以及为何连接复用比大多数人想象的更重要。你无法优化那些你不知道存在的东西。
第3步:HTTP 主要是文本
一个典型的请求如下所示:
GET /api/documents/123 HTTP/1.1
Host: collabspace.app
Authorization: Bearer
没有函数,没有对象——只有在连接上传输的结构化文本。框架隐藏了这些细节,但当请求头、方法或状态码出错时,错误会泄露出来。
第4步:你的后端不是“服务器”
请求并不会神奇地直接命中你的 Next.js 路由。它会经过多个层级:
- 负载均衡器
- 反向代理(nginx、Vercel Edge 等)
- 应用服务器
- 中间件
- 路由处理程序
- 数据库 / 缓存
当出现问题时,通常不是你的代码,而是你忘记存在的某一层。
第5步:默认无状态(真的)
每个 HTTP 请求都是独立的;服务器不会记住用户。我们通过以下方式模拟记忆:
- Cookies
- Headers
- 令牌(JWT 等)
- 会话
- Redis(或其他存储)
如果你曾经想过为什么身份验证会让人感到烦恼——这就是原因。网络从未为“已登录用户”设计;我们后来才把它们硬改进去。
第6步:响应只是决定
响应由以下组成:
- 状态码
- 头部
- 主体
示例:
HTTP/1.1 200 OK
Content-Type: application/json
{ "id": 123, "title": "Document" }
状态码不是装饰。错误使用它们会破坏缓存、重试机制,并导致客户端行为异常。许多“随机的前端错误”实际上是后端响应不当导致的。
WebSockets 改变游戏规则
HTTP 遵循 请求 → 响应 → 完成 的模式,这并不适合实时应用。WebSockets 的工作方式不同:
- 打开一个连接。
- 保持连接存活。
- 双向发送消息。
这就是协作应用、聊天和多人工具的运行方式——使用持久连接,而不是 “实时 API”。一旦你理解了这一点,WebSockets 就不再让人感到害怕。
为什么这对“full‑stack”很重要
如果在没有这些知识的情况下构建 full‑stack 应用:
- Debugging feels random. → 调试感觉随机。
- Performance feels mystical. → 性能感觉神秘。
- System design feels abstract. → 系统设计感觉抽象。
掌握基础后:
- Logs make sense. → 日志变得有意义。
- Network tabs become useful. → 网络标签页变得有用。
- Architecture decisions get easier. → 架构决策变得更容易。
这些知识终身受益。
我的经验法则
如果你无法解释:
- DNS
- TCP vs. HTTP
- Stateless requests
- Persistent connections
…你只是在使用网络,而不是在其之上构建。这在早期是可以的,但在某个时刻,你应该跨过这条界线。
Final thought
框架很棒。抽象很有用。但浏览器和服务器并不在乎你的技术栈——它们只关心:
- 字节
- 连接
- 规则
一次学会它们;它们永远不会改变。
下一篇文章
- 从零构建实时协作应用
- 为什么我不再收集框架而开始交付
- “全栈工程师”在2026年到底意味着什么
关注以获取更少的浮夸,更多真实的工程内容。