网络排查

网络 / DNS / TLS / HTTP2026.09.1428 分钟阅读

DNS、连接、TLS、HTTP 和代理问题的排查方法。

域名无法解析或解析结果异常

先查看 DNS 回答,再对照系统解析结果。

查看状态码、记录与应答来源

dig +time=2 +tries=1 example.com A
dig +time=2 +tries=1 example.com AAAA

NXDOMAIN 表示名称不存在,NOERROR 但无记录表示该类型没有结果,SERVFAIL 表示解析失败。SERVER 是回答请求的解析器,TTL 是缓存时间。

A 对应 IPv4,AAAA 对应 IPv6,CNAME 指向另一个域名。只看地址可用 dig +short example.com A。

对照应用使用的系统解析路径

getent ahosts example.com
cat /etc/resolv.conf
grep -nE '^[[:space:]]*hosts:' /etc/nsswitch.conf
grep -nF -- "example.com" /etc/hosts

getent 使用系统 NSS 配置,会读取 hosts 等名字服务;dig 直接查询 DNS。应用或浏览器还可能使用自己的解析器。

指定解析器并复核修改结果

# 192.0.2.53 替换为当前网络可用的 DNS 解析器
dig @192.0.2.53 example.com A +time=2 +tries=1
# 怀疑 UDP 查询路径异常时,对同一解析器使用 TCP 对照
dig @192.0.2.53 example.com A +tcp +time=2 +tries=1

比较同一名称和记录类型。分区解析、CDN 调度和缓存可能返回不同地址,修改记录后要等待旧缓存过期。

连接被拒绝或一直超时

查看 curl 最后停在哪一步,再检查路由、监听和防火墙。

用有限时请求确定失败阶段

curl -q -v --connect-timeout 3 --max-time 10 https://example.com/health -o /dev/null
curl_status=$?
printf 'curl_exit=%s\n' "$curl_status"

-q 放在最前面可跳过 curl 配置文件,-v 输出连接过程。分享输出前删去认证头和 Cookie。

--connect-timeout 限制连接阶段,--max-time 限制整个请求。连接被拒绝与超时应结合最后一条日志判断。

核对客户端地址与路由选择

ip -br address
ip route get 192.0.2.10

查看目标 IP 对应的 dev、via、src。走代理时查代理地址的路由,容器中的故障在容器网络中检查。

在服务端核对监听与访问范围

# 以下在目标服务所在主机或容器内执行
ss -lntp 'sport = :443'
ip -br address

核对端口、进程和绑定地址。127.0.0.1 只接受本机 IPv4 访问;容器监听还需要相应的端口映射。

监听正常时继续查防火墙、云安全组、负载均衡和回程路由。

IPv4 正常而 IPv6 失败

分别用 IPv4 和 IPv6 请求同一域名。

确认双栈记录与本机路由

dig +time=2 +tries=1 example.com A
dig +time=2 +tries=1 example.com AAAA
ip -4 route get 192.0.2.10
ip -6 route get 2001:db8::10

IPv6 连接需要域名有 AAAA 记录,本机有可用 IPv6 地址和路由。

分别发起相同的 GET 请求

curl -q --noproxy '*' -4 -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health
curl -q --noproxy '*' -6 -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health

--noproxy '*' 绕过代理,两次请求保持相同路径和超时设置。

固定单个地址排除节点差异

curl -q --noproxy '*' --resolve example.com:443:192.0.2.10 -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health
curl -q --noproxy '*' --resolve 'example.com:443:[2001:db8::10]' -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health

--resolve 指定连接地址,同时保留域名对应的 Host、SNI 和证书校验。

TLS 握手或证书校验失败

检查本机时间、证书有效期、域名和证书链。

带正确 SNI 验证握手与主机名

date -u
openssl version
timeout 10s openssl s_client -connect example.com:443 -servername example.com -verify_hostname example.com -verify_return_error </dev/null

-servername 设置 SNI,-verify_hostname 校验域名,-verify_return_error 在证书错误时中止。timeout 返回 124 表示请求超时。

保存服务端证书并查看有效期

tls_dir=$(mktemp -d ./tls-check.XXXXXX)
timeout 10s openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null > "$tls_dir/handshake.txt" 2> "$tls_dir/handshake.err"
# 确认输出中已取得证书后读取第一张证书
openssl x509 -in "$tls_dir/handshake.txt" -noout -subject -issuer -dates -ext subjectAltName

-showcerts 列出服务端发送的证书。检查叶证书的 SAN、有效期和中间证书是否齐全。

按信任要求重新验证

内部 CA 使用 --cacert 指定证书文件:

curl -q --cacert ./company-ca.pem -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health

双向 TLS 还需要客户端证书和私钥:

curl -q --cert ./client-cert.pem --key ./client-key.pem -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health

私钥使用文件传入。内部 CA 同时加 --cacert,保留服务端证书校验。

HTTP 返回错误或反复重定向

保存响应头和正文,再根据请求 ID 查服务端日志。

分别保存响应头、正文与退出状态

http_dir=$(mktemp -d ./http-check.XXXXXX)
curl -q -sS --connect-timeout 3 --max-time 10 -D "$http_dir/headers.txt" -o "$http_dir/body.txt" -w 'status=%{http_code} ip=%{remote_ip}\n' https://example.com/health
curl_status=$?
printf 'curl_exit=%s\n' "$curl_status"
sed -n '1,40p' "$http_dir/headers.txt"

检查状态码、Content-Type 和正文。curl 默认不会因 404/500 返回非零退出码,脚本中可用 --fail 将 HTTP 错误视为失败。

按响应类型关联服务端证据

观察结果下一步检查
401/403核对身份、权限、Host 与网关策略;不能仅凭状态认定账号错误
404/405核对路径、前缀重写与请求方法;HEAD 失败不代表 GET 失败
429查看限流范围和 Retry-After,停止高频重试后再验证
502/503/504关联网关与上游日志,检查连接、健康状态和超时;响应节点可能尚未联系到应用

用请求时间、路径和请求 ID 关联应用、网关或 CDN 日志。

逐跳检查重定向目标

curl -q -sS -L --max-redirs 5 --proto-redir =https --connect-timeout 3 --max-time 15 -D - -o /dev/null -w 'final=%{url_effective} status=%{http_code} redirects=%{num_redirects}\n' https://example.com/health

最多跟随 5 次 HTTPS 重定向。循环跳转时,检查 Location、应用外部 URL 和反向代理传递的协议。

请求成功但响应缓慢

用 curl 记录各阶段时间,定位耗时发生在哪一步。

记录目标地址与分段时间

每次启动新的 curl 进程,直连同一个 URL。

curl -q --noproxy '*' -sS -o /dev/null --connect-timeout 3 --max-time 15 \
  -w 'status=%{http_code} ip=%{remote_ip} dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first=%{time_starttransfer} total=%{time_total} bytes=%{size_download} speed=%{speed_download}\n' \
  https://example.com/health
输出或差值单次直连 HTTPS 请求中的解释
time_namelookup从开始到解析完成的累计时间
time_connect − time_namelookup近似 TCP 建连耗时
time_appconnect − time_connect近似 TLS 握手耗时
time_starttransfer − time_appconnect包含请求发送、网络往返、排队与服务处理,不能全算作应用执行时间
time_total − time_starttransfer首字节后直至传输完成的时间,结合响应大小与速度判断

时间单位为秒,size_download 为下载字节数,speed_download 为平均字节每秒。差值计算用于成功完成、未重定向和未复用连接的请求。

少量重复并保持条件一致

for attempt in 1 2 3; do
  date -u +%FT%TZ
  curl -q --noproxy '*' -sS -o /dev/null --connect-timeout 3 --max-time 15 -w 'status=%{http_code} ip=%{remote_ip} first=%{time_starttransfer} total=%{time_total}\n' https://example.com/health
  sleep 2
done

使用同一客户端、路径和认证条件重复请求,并记录节点 IP 和响应大小。

按慢阶段安排下一次对照

DNS 慢时对比解析器;连接或握手慢时对比地址族和节点;首字节慢时查网关和应用日志;下载慢时对比响应大小和压缩方式。

加 --http1.1 可与默认协商的协议作对照。

代理、CDN 与源站结果不一致

分别请求代理、公开域名和源站,比较响应。

对照默认代理路径与直连路径

curl -q -v --connect-timeout 3 --max-time 10 https://example.com/health -o /dev/null
# 仅在允许直连目标的网络中做以下对照
curl -q --noproxy '*' -v --connect-timeout 3 --max-time 10 https://example.com/health -o /dev/null

查看是否经过环境代理、出现 CONNECT,以及实际连接地址。http_proxy 使用小写,NO_PROXY/no_proxy 指定绕过代理的地址。

固定源站地址并保留站点身份

# 192.0.2.10 必须是已确认且允许直接访问的源站地址
curl -q --noproxy '*' --resolve example.com:443:192.0.2.10 -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health

--resolve 绕过 DNS 选择,保留 Host、SNI 和证书校验。源站限制回源地址或要求客户端证书时,直接访问会被拒绝。

源站端口不同时,用 --connect-to 将连接转到 8443,URL 仍保留 example.com:443。

curl -q --noproxy '*' --connect-to example.com:443:192.0.2.10:8443 -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 https://example.com/health

核对缓存、版本与回源记录

比较状态码、Age、ETag、Cache-Control 和版本标记。Age 表示缓存中的时间,ETag 用于验证响应内容。

结合 CDN 规则和源站日志确认是否回源。需要清缓存时,使用 CDN 的缓存清理功能。

其他

辅助观察 ICMP 与网络路径

# 需安装 iputils;仅用于辅助观察
ping -c 4 -W 2 192.0.2.10
timeout 15s tracepath -n 192.0.2.10

ping 检查 ICMP 连通性,tracepath 查看探测路径。中间节点可能过滤或限速探测报文。

区分浏览器失败与命令行成功

浏览器报错而 curl 成功时,查看开发者工具中的请求、预检和控制台错误,再检查代理、缓存与登录状态。跨域读取还受浏览器同源策略限制。

# api.example.com 与页面来源需替换为实际域名;仅检查预检响应头
curl -q -sS -D - -o /dev/null --connect-timeout 3 --max-time 10 -X OPTIONS \
  -H 'Origin: https://example.com' \
  -H 'Access-Control-Request-Method: GET' \
  https://api.example.com/health

核对 Access-Control-Allow-Origin、允许的方法和头部;携带凭证时还需要对应的凭证设置。

记录可复现的排查证据

date -u +%FT%TZ
curl --version
openssl version
ip -br address

记录客户端环境、请求路径、代理、目标 IP、错误和退出状态。分享记录前去掉认证头、Cookie 和敏感响应。

— 本页完 —
← 返回笔记列表

搜索内容

输入关键词搜索文章、笔记与随笔。