首页
关于本站
私人 DNS 加密设置教程
全部文章
推荐
加密 DNS 服务
DNS 实时战报
过滤规则订阅
搜索
网络与 DNS
加密 DNS 实操
路由器与家庭网络
自建服务
AdGuard Home
服务器运维
开发与工具
安全与隐私
登录
搜索
23-4.CN
累计撰写
59
篇文章
累计收到
3
条评论
首页
分类
网络与 DNS
加密 DNS 实操
路由器与家庭网络
自建服务
AdGuard Home
服务器运维
开发与工具
安全与隐私
页面
关于本站
私人 DNS 加密设置教程
全部文章
推荐
加密 DNS 服务
DNS 实时战报
过滤规则订阅
注册丨登录
搜索到
1
篇
标签为
TLS
的文章
2026-10-24
curl --resolve 与 --connect-to 的区别:只换连接目标,不换 SNI 和 Host
这篇是排查工具笔记,和 DNS 加密本身无关。站内的加密 DNS 服务在 dns.23-4.cn。有三种场景会让你需要「连到某个特定 IP,但让 TLS 和 Host 头还是原来的域名」:换了服务器,DNS 还没切,想先在新机器上验证站点能不能正常起来;怀疑 DNS 解析到了旧 IP,想证明「问题在解析,不在服务」;同一台机器上跑了多个虚拟主机,想精确命中其中一个。/etc/hosts 能解决第一种,但它要 root、会影响整机、还要记得改回来。curl 有两个参数是专门为这件事设计的:--resolve 和 --connect-to。它们看起来像同一件事,实际差别很关键——用错了会在证书和 SNI 上踩坑,而且报错信息完全指不到真正的原因。下面的输出都是我在一台 Ubuntu 20.04、curl 7.68 上实际跑出来的。一、先看手册怎么说两条命令的 man 原文,差别一眼就能看出来。--resolve:--resolve <host:port:address[,address]...> Provide a custom address for a specific host and port pair. Using this, you can make the curl requests(s) use a specified address and prevent the otherwise normally resolved address to be used. Consider it a sort of /etc/hosts alternative provided on the command line. The port number should be the number used for the specific protocol the host will be used for.--connect-to:--connect-to <HOST1:PORT1:HOST2:PORT2> For a request to the given HOST1:PORT1 pair, connect to HOST2:PORT2 instead. This option is suitable to direct requests at a specific server, e.g. at a specific cluster node in a cluster of servers. This option is only used to establish the network connection. It does NOT affect the hostname/port that is used for TLS/SSL (e.g. SNI, certificate verification) or for the application protocols. "HOST1" and "PORT1" may be the empty string, meaning "any host/port". "HOST2" and "PORT2" may also be the empty string, meaning "use the request's original host/port".关键就那一句:--connect-to 只管建立网络连接,不影响 TLS/SSL 用的主机名和端口(也就是 SNI、证书校验),也不影响应用层协议。--resolve 手册里没写这句,但它的行为是一样的——它替换的只是「域名到地址」的解析结果,SNI 和 Host 头仍然来自你请求的 URL。一句话概括两者: 改连接目标能改端口SNIHost 头证书校验用哪个名字--resolve✅❌(只能匹配,不能换成别的端口)不变不变URL 里的域名--connect-to✅✅不变不变URL 里的域名裸 IP 访问✅✅变成 IP变成 IPIP,几乎必然失败二、--resolve:命令行版的 /etc/hosts先看基准。不加任何参数:curl -sS -o /dev/null -w 'code=%{http_code} ip=%{remote_ip}\n' https://www.23-4.cn/实测:code=200 ip=240e:95d:3020:43::4006注意 remote_ip 是个 IPv6 地址。这台机器同时有 A 和 AAAA 记录,curl 优先选了 IPv6。这本身就是个坑——你想测 IPv4 那台的时候,可能一直在打 IPv6。现在强制走 IPv4:curl -sS -o /dev/null -w 'code=%{http_code} ip=%{remote_ip}\n' \ --resolve www.23-4.cn:443:160.202.238.204 \ https://www.23-4.cn/实测:code=200 ip=160.202.238.204remote_ip 变成了 160.202.238.204,状态码还是 200。说明:连接确实打到了指定 IP,而 TLS 握手、SNI、Host 头全都还是 www.23-4.cn——所以服务端正常返回了这个站点的内容。--resolve 的格式是 host:port:address,端口必须写,而且必须和 URL 里的端口一致(https 就是 443)。它匹配的是「主机 + 端口」这个组合,所以同一个主机不同端口要写多条:curl --resolve www.example.com:443:10.0.0.1 \ --resolve www.example.com:8443:10.0.0.2 \ https://www.example.com/三、--connect-to:能换端口,这是它真正的价值--resolve 不能表达「连到 443 但从 9999 端出去」。--connect-to 可以,格式是 HOST1:PORT1:HOST2:PORT2。先验证端口真的生效——把它指到一个没人监听的端口:curl -sS -o /dev/null -w 'code=%{http_code}\n' \ --connect-to www.23-4.cn:443:160.202.238.204:9999 \ https://www.23-4.cn/实测:curl: (7) Failed to connect to 160.202.238.204 port 9999: Connection refused code=000报错里明确是 port 9999。这就证明了 PORT2 被真正用上了,不是摆设。同样的思路可以证明 --resolve 真的改了目标 IP。故意指一个不可达地址:curl -sS -o /dev/null -w 'code=%{http_code}\n' \ --resolve www.23-4.cn:443:192.0.2.1 \ https://www.23-4.cn/实测:curl: (28) Connection timed out after 6001 milliseconds code=000192.0.2.1 是 RFC 5737 保留的文档用地址,永远不可达。超时说明 curl 真的去连它了,而不是忽略了参数。四、最容易踩的坑:SNI 不会跟着变这是两个参数最反直觉的地方,也是它们的设计意图。curl -sS -o /dev/null -w 'code=%{http_code}\n' \ --resolve example.com:443:160.202.238.204 \ https://example.com/实测:curl: (60) SSL: no alternative certificate subject name matches target host name 'example.com'TLS 握手阶段就失败了。因为 SNI 发出去的是 example.com,而对方服务器出示的证书是 *.23-4.cn:timeout 8 openssl s_client -connect www.23-4.cn:443 -servername www.23-4.cn </dev/null 2>/dev/null \ | openssl x509 -noout -subject -ext subjectAltName实测:subject=CN = *.23-4.cn X509v3 Subject Alternative Name: DNS:*.23-4.cn, DNS:23-4.cnexample.com 不在这个证书的 SAN 里,校验就过不去。这个「失败」恰恰证明参数工作正常:--resolve 只换了「连哪里」,没换「我是谁」。如果它把 SNI 也一起换了,你反而失去了用它验证真实站点的能力——因为那样测的就不是你的配置,而是另一个域名的配置了。想看这个错配后面的真实响应,加 -k:curl -sS -o /dev/null -w 'code=%{http_code}\n' -k \ --resolve example.com:443:160.202.238.204 \ https://example.com/实测:code=301301 是因为请求落到了默认虚拟主机,被重定向走了。这就是「Host 不匹配」的典型表现:TLS 能过(因为 -k 跳过了校验),但 nginx 找不到对应的 server_name,于是交给默认 server 处理。五、和「直接写裸 IP」有什么区别很多人图省事直接写 IP:curl -sS -o /dev/null -w 'code=%{http_code}\n' -k https://160.202.238.204/实测:code=301看起来和上面一样,但两者不是一回事: --resolve / --connect-to裸 IP连接目标指定 IP就是这个 IPSNIwww.23-4.cn无(或 IP)Host 头www.23-4.cn160.202.238.204命中哪个 server_name正确的那个默认 server证书校验正常通过必然失败,只能 -k用裸 IP 测出来的结果不能代表真实用户拿到的结果。它走的是默认虚拟主机,可能是另一个站、可能被重定向、可能直接 404。用它来判断「新机器部署好了没」会得到完全错误的结论。这也是为什么我在 nginx 配置里留了这么一段注释:# curl --resolve dns.23-4.cn:443:160.202.238.204 ... /dns-query -> 200 ✓ # curl https://160.202.238.204/dns-query(裸 IP、无 SNI) -> 301 ✗ # curl --resolve foo.23-4.cn:443:160.202.238.204 ... /dns-query -> 301 ✗第二条是裸 IP,第三条是证书能过但 server_name 不存在的子域名——注意 foo.23-4.cn 是能通过证书校验的(*.23-4.cn 覆盖它),所以它返回 301 而不是 TLS 错误。这两种 301 长得一样,原因完全不同。六、什么时候用哪个场景用哪个理由验证新机器上的站点能否正常响应--resolve不需要改端口,最直观想测某个非标准端口上的服务--connect-to唯一能改端口的复现「DNS 解析到了旧 IP」--resolve精确指定旧 IP测内网某台机器上的同域名服务--connect-to常常端口也不一样(比如 8080)排查「是不是 SNI 的问题」两者都行 + openssl s_client -servernamecurl 不方便单独控制 SNI想故意构造 Host 不匹配裸 IP 或改 -H 'Host:'这两个参数做不到如果只是想让整台机器临时认某个 IP,用 --resolve 就够,别去改 /etc/hosts——改完忘了删,是另一类故障的开头。七、验证你到底连到了哪排查时最容易犯的错是:以为参数生效了,其实打到了别的地方。用 -w 把连接信息打出来,别靠猜。curl -sS -o /dev/null \ -w 'http=%{http_code} ip=%{remote_ip} port=%{remote_port} time=%{time_total}\n' \ --resolve www.23-4.cn:443:160.202.238.204 \ https://www.23-4.cn/%{remote_ip} 是关键字段——它告诉你实际连到哪个地址。配合 %{remote_port} 可以确认端口,%{time_connect} 能看出握手耗时是否异常。想看得更细,用 -v,重点看这三行:* Connected to www.23-4.cn (160.202.238.204) port 443 (#0) * TLSv1.3 (OUT), TLS handshake, Client hello (1) * Server certificate: * subject: CN = *.23-4.cn第一行证明「连到哪个 IP、哪个端口」,第二、三行证明「TLS 是跟谁握手的」。如果 Connected to 后面的 IP 不是你指定的那个,说明参数没生效(最常见原因是 port 写错,比如 URL 是 https:// 但 --resolve 里写了 :80)。踩坑点汇总:--resolve 的端口必须和 URL 一致。https:// 对应 443,写成 80 会静默失效——curl 找不到匹配项,就退回正常解析,你还以为参数起作用了。IPv6 优先。目标站点同时有 A/AAAA 时,不加 --resolve 可能一直在打 IPv6,看起来「换了机器没用」。加 -4 或 --resolve 强制指定。--connect-to 的空字符串表示「任意」或「沿用原值」。--connect-to ::10.0.0.1:8080 表示「所有主机所有端口都连到 10.0.0.1:8080」——调试方便,但很容易连错自己都不知道。两者都不改变 Host 头。要测「Host 头不对会怎样」,得用 -H 'Host: other.example.com',那是另一条路径。相关阅读怎么验证加密 DNS 真的生效:dig +tls / +https 与 curl 四步逐层排查网站被误拦了怎么办:怎么确认是 DNS 拦的,以及反馈后多久能修好HTTP 的 Vary 和缓存键:CDN 上那些「明明改了却没生效」
2026年10月24日
16 阅读
0 评论
0 点赞