SNI 让一个 IP 承载多个 HTTPS 站点
Server Name Indication(简称 SNI)在 TLS 握手中把目标主机名发给服务器,使同一个 IP 地址能够承载多张证书、多个站点。没有 SNI 时,分配不同地址是一种办法,却不是唯一办法:一张包含多个主体备用名称(SAN)的证书,或覆盖相应名字的通配符证书,也可以供多个站点共用。问题在于先后顺序:在不使用 early data 的完整证书型 HTTPS 握手中,服务器选证书发生在收到标识应用站点的加密 HTTP 请求之前,不能等读到 Host 再为这次握手换证书。 本文关注 nginx 的配置边界:哪个 server 块提供证书,名字没有匹配时采取什么策略,以及诊断命令究竟证明了什么。选中证书、客户端验证身份、把请求送到应用,是三个需要分别检查的决定,不能把其中一项成功当作另外两项也成功。 名字最先报出 客户端在 ClientHello 中携带 SNI,nginx 据此选择 TLS 服务配置。这个扩展使用已有的握手报文,不需要额外发送一次请求来询问站点。不过,把全过程说成“发生在加密之前”并不准确:TLS 1.3 在 ServerHello 之后加密证书报文。这里重要的是名字在证书选择时已经可用,而不是证书或整个握手始终明文传输。 客户端必须发送 SNI,服务器才能在这个阶段按名字选择站点。不带 SNI 时,nginx 从对应监听地址与端口的缺省服务配置开始处理;该配置可能提供证书,也可能拒绝握手。若收到证书,客户端仍需检查信任链以及自己预期的身份。收到一张证书不等于接受它,也不等于已经知道后续请求会进入哪个应用。 用 server_name 匹配 nginx 在相关监听地址和端口的 server_name 条目中匹配 SNI:精确名优先,然后是最长的前置星号通配名、最长的后置星号通配名,最后是配置顺序中第一个匹配的正则表达式。证书和私钥配置可以继承,也可以由多个站点共用;并不存在每个名字必须单独持有一张证书的规则。 ...