Caddy 纯 IP 部署自签名 HTTPS 报错 TLS 握手失败的解决方案

签名 HTTPS 报错 TLS 握手失败的终极解法

在使用 Caddy 为纯 IP 服务器(如内网环境或测试服)配置自签名 HTTPS 时,很多人会发现即使配置了 tls internal,浏览器依然无法访问,后台日志疯狂报错 TLS handshake error。本文将分析该问题的底层原因,并提供最简的解决方案。

表现症状

当你通过浏览器访问纯 IP 的 HTTPS 站点时,页面提示连接被重置或 SSL 协议错误。查看 Caddy 日志,会出现如下关键报错:

Plaintext

"msg":"no matching certificates and no custom selection logic","identifier":""
"msg":"http: TLS handshake error ... no certificate available for '<你的服务器IP>'"

为什么 tls internal 会失效?

核心原因在于 SNI(服务器名称指示)丢失

在 HTTPS 握手阶段,SNI 用来告诉服务器客户端想访问哪个域名,以便服务器返回对应的证书。然而,当浏览器直接请求纯 IP 地址时,底层协议不会发送 SNI

Caddy 接收到这类“没带名字”的请求后,因为拿不到明确的标识符,无法在自己的配置库里匹配到那张 tls internal 生成的自签名证书,最终只能因为找不到证书而直接中断连接。

解决方案:使用 default_sni 兜底

既然浏览器不发 SNI,我们只需在 Caddy 的全局配置中,强制把所有无 SNI 的请求都默认当作对本机 IP 的请求。同时,站点块的名称必须显式绑定这个具体的 IP,绝不能使用 https://:8443 这种简写。

终极 Caddyfile 配置模板

修改你的 Caddyfile,参考以下标准结构(请将 <你的服务器IP> 替换为你实际访问的公网或局域网 IP):

Plaintext

{
  # 核心救命稻草:强制将没有 SNI 的瞎请求,全部指定为该 IP
  default_sni <你的服务器IP>
}

# 必须显式写出完整的 IP 和端口,与 default_sni 完全一致
<你的服务器IP>:8443 {
  # 启用 Caddy 内部自签名证书
  tls internal
   
  # 你的业务逻辑,比如反向代理
  reverse_proxy localhost:5244
}

# 多个端口可以并行配置
<你的服务器IP>:9443 {
  tls internal
  reverse_proxy localhost:3001
}

# 80 端口 HTTP 服务不受影响,按需配置
:80 {
  root * /usr/share/caddy
  file_server
}

修改完成后,重载配置即可:

Bash

caddy reload --config Caddyfile

总结

配置生效后,浏览器再通过纯 IP 访问时,TLS 握手就能顺利完成。由于使用的是自签名证书,浏览器仍会提示“您的连接不是私密连接”,点击“高级 -> 继续访问”即可正常打开网站。

💡 进阶建议: 如果你的服务器开放了外网的 80 和 443 端口,并且你希望彻底消除浏览器的“不安全”红锁警告,可以考虑将配置中的 IP 直接替换为 <你的服务器IP>.nip.io,并删掉 tls internal 这一行。Caddy 会自动利用这个魔法域名为你申请真实的 Let’s Encrypt 公网证书,实现全自动的绿色安全锁。

Bookmark the permalink.

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注