陈小涛的技术学习笔记

HTTP 与 HTTPS 工作原理小结

刚学做网站那阵子,我对 HTTP 的理解就一句话:「加个 s 就安全了」。真正开始自己配服务器、自己签证书、对着抓包工具一条条看报文,才发现中间发生的事情比想象的多得多,也比想象的有意思。这篇按"一次访问从头到尾"的顺序写,把这条链路上每个环节都摊开讲一遍——不追求学术严谨,追求的是"看完之后,再配 nginx 或 Caddy 心里有底"。

请求响应报文结构状态码TLS 握手证书

一、从敲下回车到看见页面,中间跑了几步

这件事我一开始完全没概念,以为"输入网址 → 页面出现"是一步。其实拆开至少有六步,每一步都能出问题。按顺序走一遍:

  1. 地址被解析成 IP。浏览器先看本地缓存里有没有记过这个域名,没有就问 DNS,拿到一个 IP 地址。这一步的耗时完全取决于缓存命中情况——本机或路由器上有没有配 DNS 缓存,体感差别巨大。
  2. 建立一条 TCP 连接。拿到 IP 之后,客户端要和对方在 443(或 80)端口上"握一次手",双方确认能通、能收能发。这一步就是常说的"三次握手",网络延迟高的时候,它就占掉了总耗时的一大块。
  3. 如果是 HTTPS,还要再握手一次。这次是 TLS 层面的,目的有两个:验证对方的身份是不是真的持证,以及商量出一个双方共用的加密方式。这一步是最容易配错的地方,后面单独说。
  4. 发出请求。客户端把"我要哪个路径、我支持什么格式、带没带凭据"这些信息组装成一段文本发出去。这段文本就是 HTTP 报文,结构非常朴素,后面贴给你看。
  5. 对方处理并返回。服务器上的程序(可能是静态文件,也可能是自己写的程序)根据路径决定给什么,然后把结果连同状态码、内容类型一起回过来。
  6. 浏览器负责渲染。拿到 HTML 之后,浏览器再按里面的引用去要 CSS、图片、脚本,一轮轮重复上面的过程。所以打开一个页面其实不是一次请求,而是十几次甚至几十次。
浏览器 发起方 DNS 域名→IP 连接 + TLS 握手、验证 服务器 处理并回应 响应沿原路返回 → 浏览器渲染 ① 域名解析 → ② 建连接 → ③ TLS 握手 → ④ 发请求 → ⑤ 收响应 其中任何一环卡住,体感都是"网站打不开",但原因完全不同
同样是"打不开",卡在第几步,排查方向完全不一样

理解这条链路最大的好处,是排查问题时不再瞎猜。打不开页面的时候,我会按这个顺序问自己:域名能不能解析?端口通不通?证书有没有过期?内容路径对不对?四问下来,问题基本就定位了。

二、请求和响应到底长什么样

很多人以为 HTTP 报文是某种二进制协议,其实它就是纯文本——几行头信息加一个空行,后面跟正文。用 curl 看一眼就明白了:

# 看请求和响应头,-I 表示只要头不要正文
curl -I https://example.com

# 结果长这样:
HTTP/2 200                      ← 状态行
content-type: text/html; charset=utf-8
content-length: 1256
cache-control: max-age=3600
date: Wed, 24 Sep 2026 12:00:00 GMT

结构就是三块:状态行(协议版本 + 状态码 + 说明)、头信息(键值对,一行一个)、空行之后是正文。请求也是同样的结构,只是第一行变成了「方法 + 路径 + 版本」:

GET /articles/linux.html HTTP/1.1     ← 方法 + 路径 + 版本
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html
Accept-Encoding: gzip
                                      ← 这个空行很关键,它分隔头部和正文
(正文:POST 时才有)

方法里最常用的就四个,各自的分工其实很好记:

方法语义有没有正文典型场景
GET读取无打开页面、取列表、下载文件
POST提交有表单提交、上传数据
PUT / PATCH更新整体 / 局部有修改一条已有记录
DELETE删除无删掉一条记录
一个我吃过亏的细节:GET 的查询参数会完整出现在地址栏和日志里,所以凡是带凭据的东西,一概不能放 GET。我早期的配置里图省事把一串密钥写在地址里,后来看访问日志才发现全躺在里面。

三、状态码记五类就够,不用记一百个

状态码看起来很多,规律却特别简单——第一位数字就说明了结局的大类。记住五个大类,具体数字用到再查:

范围含义日常最常碰到的
1xx信息,连接还在协商几乎见不到,遇到说明用了新的传输方式
2xx成功200 正常;204 成功但没有内容
3xx跳转301 永久跳(换域名用);302 临时跳;304 缓存还有效,别再传了
4xx请求方的问题403 权限不足;404 路径不存在;429 请求太频繁被限流
5xx服务器自己的问题500 程序抛异常;502 反代后面那个程序没起来;504 后面那个程序响应超时
一个实用经验:404 和 403 的分工最能反映配置水平。404 是"没这个路径",403 是"有这个东西但不让你看"。配静态站点的时候如果整站都报 403,八成是目录缺了执行权限或者没有默认首页——不是文件不存在。

四、HTTPS 到底多做了什么

HTTPS 等于「HTTP 报文」被塞进了一个加密通道里运输,通道本身由 TLS 提供。它要解决三个问题:内容别被人看、内容别被人改、对面确实是它本人。前两个靠加密,第三个靠证书。

客户端 服务器 ① 我支持这些加密方式 ② 这是我的证书(含公钥 + 签发方签名) ③ 用它的公钥加密一个临时密钥送过去 ④ 双方都算出同一把会话密钥 → 开始加密通信 全程只有第 ① 步是明文,之后都是密文
握手的目的只有一个:安全地商量出一把双方共用的钥匙

证书里装的是三样东西:这个域名、一把公钥、签发机构的签名。浏览器内置了一批受信任的签发机构名单,只要能顺着证书链验到名单里的某一个,就认为"这个域名确实由它持有"。整条链路里最关键的一点是:证书必须用域名去签——给 a.example.com 签的证书,拿去给 b.example.com 用,浏览器会直接报错。

省心的做法:用 Caddy 之类自带证书管理的反向代理,第一次启动就自动申请、到期前自动续期,全程不用手动碰。如果想手动控制,Let's Encrypt 签的免费证书也完全够用——证书本身是免费的,收费的是"人工支持"和保险。

五、配 HTTPS 时我踩过的几个坑

证书装好了,但打开还是报"不安全"。
最常见的原因是证书链不全。签发机构会给两个文件:你的证书和它的中间证书。只配了前者,部分浏览器能过、部分不能过,表现就是"我这儿好的,别人打不开"。正确做法是把两张拼在一个文件里再配。
同一个 IP 上挂多个域名,证书怎么办?
用带子域名的泛域名证书,或者配多个证书让反向代理按域名自动挑。关键前提是:客户端要支持在握手阶段告诉对方"我要访问哪个域名",现代浏览器都支持,所以只要配置正确就不用担心选错证书。
明明配了 443,为什么有的客户端还是连不上?
先查两处:一是服务商后台的防火墙规则有没有放行 443,这一层在操作系统之外,系统里怎么折腾都没用;二是地址里有没有误带端口号,有些客户端程序只允许标准端口,写了 :443 反而不认。
怎么快速判断是证书的问题还是程序的问题?
在本机直接打自己的 IP 或本机地址,比如 curl -I http://127.0.0.1:8000。能通说明程序没问题,问题在证书或反代这一层;不能通说明程序本身没起来。这一招能把排查范围一次砍掉一半。

小结

把这条链路理清楚之后,我的实际收益是三件事:

  • 配反向代理不再是复制粘贴。知道每个配置项在链路哪一环起作用,出问题知道去哪找。
  • 排错有了固定顺序。域名解析 → 端口连通 → 证书验证 → 路径匹配,四步走完问题必现形。
  • 明白"https 不是可选项"。对外的访问入口现在基本都强制加密,而且是标准端口,这不是趋势,是当下的门槛。