Debian apt 卡在 “Waiting for headers”(「等待响应头」):根因排查实战指南

你运行 sudo apt update,结果它不动了。进度条停在 0% [Waiting for headers]23% [Waiting for headers],光标先闪十秒,再闪三十秒,然后两分钟过去了。最后,要么连接超时并报出 Connection failed,要么更糟——命令就这么一直挂着,直到你按下 Ctrl-C,顺便怀疑系统是不是对你有意见。

关键在于:APT 本身并没有坏。连接已经建立,请求也已发出,服务器同样收到了。只是响应头始终没回来。“TCP 已连通,却没有响应”这个细节,就是整套排查的核心。下面遇到的问题都绕不开同一套五层网络栈,你要做的,就是找出到底哪一层在悄悄丢包。

这套顺序是我在每台卡住的主机上都用过的实战流程。我们从成本最低的信号(调试日志)开始,一路查到最糟的情况(公司防火墙改写了你的 MTU)。按顺序逐项执行,直到某一步让情况出现变化。

太长不看——只想现在就恢复正常

Bash
# Force APT onto IPv4 — fixes ~70% of stuck-header cases on dual-stack hosts
printf 'Acquire::ForceIPv4 "true";n' | sudo tee /etc/apt/apt.conf.d/99force-ipv4
sudo apt clean && sudo apt update

如果这招有效,说明你的 IPv6 已经坏了,只是 DNS 还觉得它好得很。往下翻到 “第 2 层:双栈主机上的 IPv6 故障”,看看怎么正经修好它。

如果没用,那就继续往下查。

第 1 步:先找出它卡在哪里

动任何配置之前,先打开调试输出运行 APT。这一条命令就能告诉你,罪魁祸首到底在哪一层——DNS、TCP 连接、TLS,还是 HTTP 响应本身。

Bash
sudo apt -o Debug::Acquire::http=true -o Debug::Acquire::https=true update

留意下面这些信号:

调试输出中的现象 出问题的层
Connecting to ... 之前卡住 DNS——解析器找不到镜像源
出现 Connecting to <ipv6-address> 后没动静 IPv6 路由——看第 2 步
显示 Connected to ...,但后面没有 GET /dists/.../InRelease TLS / SNI——公司中间盒吞掉了握手
已发送 GET /dists/.../InRelease,但没有响应 MTU / PMTUD——看第 3 步
有些主机能用,有些不能 镜像源本身——看第 5 步

然后看看当前配置到底用了哪些镜像源:

Bash
grep -R "^deb|^URIs:" /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

再看看解析器返回了哪些地址:

Bash
getent ahosts deb.debian.org

如果同时看到 2a04:4e42::...151.101.x.x,说明你用的是双栈。先记住这一点。

第 2 步:双栈主机上的 IPv6 故障(最常见的原因)

经典场景是这样的:VPS 模板在启动时启用了 IPv6,内核也开开心心地配好了链路本地地址,/etc/resolv.conf 还能返回 AAAA 记录——但实际上根本没有能通往公网的 IPv6 路由。也许数据中心的 v6 部署还没收尾,也许防火墙丢弃了 ICMPv6,也可能是上游提供商悄悄把你的前缀扔进了黑洞。

APT 很尽职地优先尝试 AAAA 地址,打开 TCP 连接(而且会成功,因为 IPv6 在本机看起来确实“已启用”),发出 GET,然后就开始无限等待——数据包早已掉进路由黑洞。

快速测试:直接对比 v6 和 v4 的请求:

Bash
curl -6 -I --max-time 10 http://deb.debian.org/debian/dists/bookworm/InRelease
curl -4 -I --max-time 10 http://deb.debian.org/debian/dists/bookworm/InRelease

如果 -6 卡住,而 -4 返回 200 OK,问题就在 IPv6。下面几种修法任选其一:

方案 A——仅针对 APT 的临时绕过(最快也最稳妥):不动内核,只让 APT 优先使用 IPv4。

Bash
printf 'Acquire::ForceIPv4 "true";n' | sudo tee /etc/apt/apt.conf.d/99force-ipv4

方案 B——让整个系统优先使用 IPv4(使用 gai.conf):让全系统的解析器都优先选择 IPv4。编辑 /etc/gai.conf,取消下面这行的注释:

Bash
precedence ::ffff:0:0/96  100

方案 C——彻底禁用 IPv6(核弹级操作):编辑 /etc/sysctl.d/99-disable-ipv6.conf

Bash
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

运行 sudo sysctl --system 使配置生效,然后验证:

Bash
cat /proc/sys/net/ipv6/conf/all/disable_ipv6   # should print 1
getent ahosts deb.debian.org                   # should show only IPv4

追根究底的方案 D——真正修好 IPv6 路径:如果你的 VPS 本来就承诺支持 v6,那就提交工单。问题要在上游解决,可能是 BGP 路由、防火墙规则或 MTU。别长期拿临时绕过当修复,否则下次有程序尝试 AAAA 时,你还会再踩一遍坑。

第 3 步:MTU 和 PMTUD(没亲眼见过的人通常不信)

这是个沉默杀手。小数据包都能通过——DNS 正常,TCP 能建立,GET 也离开了你的机器。但服务器返回的数据更大,超过了实际路径允许的大小,也就是说路径 MTU 低于接口 MTU。路径 MTU 发现(PMTUD)本该探测到这种情况,可它依赖 ICMP frag-needed 数据包顺利返回你的主机。

公司防火墙经常直接丢 ICMP。云负载均衡器会这么干,一些 ISP 也不例外。一旦 ICMP 回不来,你的主机根本不知道数据包太大,只会继续按 1500 重传,苦等一个永远不会出现的响应。

验证方法:检查到镜像源的实际路径 MTU,而不是只看接口 MTU:

Bash
# Replace with the real mirror host your sources.list points to
tracepath -n deb.debian.org
# Or for a specific MTU probe:
ip route get $(getent ahostsv4 deb.debian.org | awk '{print $1}' | head -1)

你应该能看到 pmtu=1500pmtu=1464 之类的行(1464 就是 1500 减去常见隧道/GRE 开销)。如果 tracepath 显示 pmtu=1500,但 curl 小文件正常、大文件却卡住,那就是 PMTUD 出问题了。

下面按影响从小到大列出解决办法:

方案 A——调低接口 MTU。如果主机流量要经过 VPN、GRE、VXLAN 或 PPPoE 隧道,就把 MTU 调成隧道封装后路径能承受的值:

Bash
# Find the right MTU from tracepath, then:
sudo ip link set dev eth0 mtu 1464

要让它重启后依然生效,请编辑 /etc/network/interfaces.d/eth0.cfg(Debian),或者修改 NetworkManager 连接配置(nmcli con mod ... 802-3-ethernet.mtu 1464)。

方案 B——通过 iptables 限制 TCP MSS。如果 MTU 改不了(比如是云 VM,路由器也不归你管),可以限制 TCP SYN 的 MSS,让连接协商出更小的分段大小:

Bash
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN 
    -j TCPMSS --clamp-mss-to-pmtu

如果处理的是本机发出的流量,而不是转发流量,请把 FORWARD 换成 OUTPUT

方案 C——在主机上禁用 PMTUD(最后手段)。这会强制所有数据包允许分片。它确实能用,但差不多等于因为烟雾报警器老响,就把它直接扔了:

Bash
sudo sysctl -w net.ipv4.ip_no_pmtu_disc=1

把配置持久化到 /etc/sysctl.d/99-no-pmtu.conf。真正的修复,是允许 ICMP frag-needed 通过——去找负责防火墙的人聊聊吧。

第 4 步:HTTP 代理、公司中间盒,以及“昨天还好好的”

有三件事能让原本正常的 APT 突然挂死,而且你的机器明明什么都没改:

  1. 有人配置了代理。检查 apt-config dump | grep -i proxy/etc/apt/apt.conf.d/*proxy*。如果环境里设置了 http_proxy,但 APT 没有采用它,请运行 sudo -E apt update,把环境变量传进去。
  2. IDS/IPS 正在丢弃请求。某些公司的“安全”中间盒看到 APT 连续快速发送 HTTP GET,就会悄悄对你限速。换个网络试试,比如手机热点或家里的网络;如果换了就正常,锅就在办公网络。
  3. 镜像源的 HTTP/1.1 范围请求支持有问题。有一类已知 bug:APT 为部分内容发送 Range: 响应头,镜像源返回 416 Range Not Satisfiable,然后 APT 就……一直等。2024 年一篇 Server Fault 报告给出的修法是:

Bash
sudo apt-get install apt    # sometimes fixes a stale client-side cache
sudo apt-get update

如果还是没用,就换镜像源——看第 5 步。

要确认中间盒是不是罪魁祸首,就在卡住时用 tcpdump 盯住这条连接:

Bash
sudo tcpdump -n -i eth0 host deb.debian.org and port 80

你会看到 SYN、SYN-ACK 和自己发出的 GET,之后要么什么都没有(数据包被黑洞吞了),要么从意料之外的位置冒出一个 RST(中间盒掐断了连接)。再和同一网络里正常工作的主机对比一下,差异会告诉你到底是谁在撒谎。

第 5 步:镜像源本身

有时候最简单的答案就是正确答案:你选的镜像源今天状态不太行。运行这个测试:

Bash
time curl -s -o /dev/null -w "%{http_code} %{time_total}sn" 
    http://deb.debian.org/debian/dists/bookworm/InRelease

然后从 /etc/apt/sources.list 换一个镜像源试试:

Bash
# Debian mirrors — pick one close to your data center
deb http://ftp.us.debian.org/debian/ bookworm main contrib
deb http://ftp.de.debian.org/debian/ bookworm main contrib
deb http://mirror.sg.debian.org/debian/ bookworm main contrib

如果换个镜像源就秒回,答案已经很清楚了。想自动挑个快的镜像源,可以安装 netselect-apt

Bash
sudo apt install netselect-apt
sudo netselect-apt bookworm

第 6 步:实用的 APT 超时与重试配置

如果你的网络环境确实不稳定(移动网络、卫星网络、酒店 wifi),上游又不是你能修的,那就调整 APT,让它尽快失败并自动重试,别一直挂着:

Bash
sudo tee /etc/apt/apt.conf.d/99short-timeouts <<'EOF'
Acquire::http::Timeout "10";
Acquire::https::Timeout "10";
Acquire::Retries "3";
Acquire::ForceIPv4 "true";
EOF

这不会修好已经坏掉的网络,但至少能避免 APT 为一个永远不会来的数据包等上两分钟,把你的终端也一起拖死。

验证:怎么确定它真的修好了

真正修好后,下面这些现象应该全部出现:

Bash
# 1. Debug output shows headers coming back
sudo apt -o Debug::Acquire::http=true update 2>&1 | grep -c 'InRelease.*200'

# 2. No "Waiting for headers" line in the progress output
sudo apt update 2>&1 | grep -c 'Waiting for headers'   # should print 0

# 3. Timing is reasonable
time sudo apt update    # should be under 30 seconds for a healthy mirror

# 4. Multiple retries all succeed
for i in 1 2 3; do sudo apt update; done

如果其中任何一项仍然卡住,说明你只补好了某个故障层。回到第 1 步,再仔细看看调试输出。

千万别这么做

  • 别用 pkill -9 apt 杀掉进程再重跑。锁文件存在是有原因的。请用 sudo fuser -vki /var/lib/dpkg/lock-frontend 安全清理。
  • 别反复清空 /var/lib/apt/lists/这会触发所有镜像源元数据的完整重新下载;如果问题和带宽有关,只会卡得更厉害。
  • 别设置 Acquire::http::Pipeline-Depth "0" 后就当问题解决了。这会彻底禁用 HTTP 流水线,你的 apt update 会慢上 3~5 倍。
  • 别一上来就怪镜像源。问题几乎总在你自己的网络栈。先用 tcpdump 确认,再宣布上游坏了也不迟。

预防检查清单

每次新装 Debian/Ubuntu 主机时,把下面这段跑一遍:

Bash
# Probe both stacks, pick the working one, lock it in
curl -6 -I --max-time 5 http://deb.debian.org/debian/ >/dev/null 2>&1 
    || printf 'Acquire::ForceIPv4 "true";n' | sudo tee /etc/apt/apt.conf.d/99force-ipv4

# Make APT fail fast instead of hanging your shell
sudo tee /etc/apt/apt.conf.d/99short-timeouts <<'EOF'
Acquire::http::Timeout "10";
Acquire::https::Timeout "10";
Acquire::Retries "3";
EOF

# Document your MTU if you're behind a tunnel
sudo tee /etc/network/interfaces.d/eth0.cfg <<'EOF'
auto eth0
iface eth0 inet dhcp
    mtu 1464
EOF

三段配置,收工。未来的你不会感谢现在的你,因为你压根不会意识到这篇文章曾经存在。


归档分类: Linux 软件 · Debian · APT · 网络
测试环境: Debian 12 (bookworm)、Debian 13 (trixie)、Ubuntu 22.04/24.04 LTS
文中使用的诊断命令: aptgetenttracepathtcpdumpipcurlsysctl

最后修改: 2026年7月13日

作者

评论

发表评论

您的邮箱地址不会被公开。