所有托管服务节省 15%

测试技能,享折扣

使用代码: Skills 开始使用
China
管理 虚拟服务器

网站宕机,但服务器可访问?从 DNS 到应用追踪故障

“SSH 正常工作,网站却不行”——从正确的问题开始

告警开始响起,用户说网站已经宕机,你的第一次测试给了你一种奇怪的安心感:SSH 仍然让你进入。那一刻感到放心,因为服务器没有消失。但这也是许多不良故障排除的开始,因为”我仍然可以登录”与”网站应该正常工作”不是一回事。

problem

有用的问题不是”我应该先重启什么?”而是”哪一层首先出现故障?”一个正常的 SSH 会话证明机器在端口 22 上是可达的。但它不能证明域名解析是否正确。它不能证明:

  • 端口 80443 是否可达
  • HTTPS 是否健康
  • Web 服务器后面的应用程序是否在响应

本指南围绕这一区别构建,并保持专注于诊断,而不是试图成为完整的 nginx、DNS、TLS、Docker 或数据库手册。

⚠️ 警告:在事件发生的最初几分钟内盲目重启 nginx、Docker、PHP-FPM 或整个 VPS 可能会抹去你需要的线索。先收集一轮证据,然后只改变实际出现故障的层。

一分钟分诊地图

info

在深入之前,先定位方向。故障的形状往往能告诉你哪一层应该首先得到关注,即使你还不知道根本原因。

把下表当作分诊快捷方式,而不是最终判决。

你看到的通常意味着首先检查什么不要假设
🧭 Could not resolve host名称未解析为地址DNS 记录、解析器路径、拼写错误Web 服务器一定是问题所在
⏱️ 超时流量被阻止、误路由或在路径后期挂起外部 curl、防火墙路径、路由、监听器可达性所有超时都意味着同一件事
🚫 Connection refused主机可达,但那里没有任何有用的服务接受连接ss -ltnp、服务状态、绑定地址整个服务器已宕机
🔐 TLS 或证书警告HTTPS 到达了 443,但身份或握手层失败提供的证书、主机名匹配、链、续期状态应用程序肯定已死亡
⚠️ 502 / 503 / 504可达的前端或服务在链的更高层失败上游交接、服务可用性、超时位置每个 5xx 错误都意味着相同的修复
🏠 本地工作但外部不工作堆栈在服务器上可能没问题,但外部路径已损坏主机防火墙、提供商防火墙、CDN 路径、路由本地成功证明了公网可达性

重要的是第一个损坏的阶段。如果 DNS 失败,后续层都是噪音。如果 443 有响应但 TLS 中断,应用还不是你的首要问题。下面的心智模型就是让这个序列感觉合乎逻辑而不是随意的原因。

为什么 SSH 不能证明网站正常运行

SSH 和网络流量是不同的路径,各有各的用途。SSH 在端口 22 上证明的是你可以通过远程管理通道到达该机器。网站依赖于端口 80443,以及它们背后的各个层级。这些是独立的测试,所以”服务器可达”和”网站可达”不是可以互换的说法。

why

最简单的理解方式是把它想象成一栋办公楼。DNS 帮助访客找到楼的地址。端口 80443 是公众访客的前台。网络服务器是接受请求并决定将其转发到哪里的接待员。应用程序是执行实际工作的办公室。数据库或其他依赖项可能位于楼的更深处。SSH 是一个完全不同的入口。它对员工很有用,但不能证明前台是开放的,也不能证明办公室的转接工作正常。

Browser
  ↓
DNS lookup
  ↓
IP address
  ↓
Port 80 / 443
  ↓
Web server
  ↓
App / upstream
  ↓
Database / dependency

当人们说一个服务在”监听”时,他们的意思是它实际上在预期的端点上接受连接。这就是将模糊的”服务器已启动”转变为可追踪的请求路径的区别。

这很重要,因为 HTTPS 可能在应用程序响应之前就失败,代理或应用程序故障可能在前端已经可达之后发生。所以下一步总是一样的:从外部先看,找到请求的最后一个成功阶段。

第1步:从服务器外部重现故障

从客户端开始,而不是从VPS内部开始。如果可能,先从另一个网络或设备进行测试,这样你就不会将本地DNS缓存、旧的/etc/hosts条目或本地防火墙/VPN问题与真正的服务器中断混淆。

使用详细的外部请求,以便你可以看到请求在失败前进行了多远:

curl -v --connect-timeout 5 --max-time 15 https://example.com/

curl -v在这里不是仅限专家使用的工具。将其理解为进度跟踪。如果它从未解析名称,你处于DNS分支。如果它连接然后显示Connection refused,主机有响应但没有有用的东西在接受那里的流量。如果它挂起直到超时,考虑过滤、路由或请求路径中更深层的挂起。如果你收到HTTP响应,即使是错误页面,你已经越过了连接层进入更高的分支。

💡 提示:尽早比较IPv4和IPv6。被遗忘的AAAA记录可能会使中断看起来不一致,因为某些客户端优先选择IPv6,而其他客户端则不然。

当双栈起作用时,为每个协议族运行一次相同的测试:

curl -4 -v --connect-timeout 5 --max-time 15 https://example.com/
curl -6 -v --connect-timeout 5 --max-time 15 https://example.com/

如果IPv4有效而IPv6失败,或反之,你已经比服务重启更快地缩小了事件范围。如果故障始于名称解析或目标选择,DNS是下一个要检查的干净分支。

第 2 步:检查 DNS 并确认正确的目标

在调试 nginx 之前,请确认该域名实际上将访问者发送到您认为的服务器。这在迁移、IP 更改、CDN 调整或部分记录编辑后尤其重要。

首先检查公共记录:

dig +short A example.com
dig +short AAAA example.com

这两行回答了一个非常实际的问题:互联网现在认为 example.com 位于何处?一个常见的故障情形是通过 IP 的 SSH 到达新 VPS,但域名仍然指向旧地址。另一个是 A 记录已更新,但 AAAA 记录仍然指向某个过时的地址。在这种情况下,只有部分流量失败。

📝 注意: curl --resolve 比在事件中期更改公共 DNS 更安全。它允许您针对您打算使用的源进行测试,同时保持主机名和 SNI 完整。

使用 curl --resolve 强制针对您期望的 IP 进行测试,而无需接触公共记录:

curl --resolve example.com:443:203.0.113.10 https://example.com/

如果这有效而公共域名仍然失败,服务器可能没问题,DNS 可能仍然是损坏的层。这里值得记住一个有限的 CDN 注意事项。如果您的源被锁定为仅接受来自 CDN IP 范围的流量,直接源测试可能会失败,仅仅因为源期望边缘流量,而不是任意公共请求。一旦确认了目标,下一个问题是是否有任何有用的东西在 80443 上应答。

第 3 步:验证 80/443 上的监听情况

现在切换到服务器并提出一个具体问题:是否有任何东西实际上在预期的端口上接受 Web 连接?机器可能是活跃的,SSH 可能工作正常,甚至 nginx 可能已安装。但公共 Web 端口上仍然可能没有有用的监听器。

首先检查监听器:

sudo ss -ltnp

:80:443 的空输出意味着没有有用的东西在监听。127.0.0.1 上的监听器意味着该服务仅接受来自本地机器的连接。0.0.0.0 上的监听器意味着它绑定在 IPv4 接口上。[::] 通常意味着 IPv6 接口。不要假设 IPv6 绑定会自动保证你需要的 IPv4 路径。

然后在更改任何内容之前使用一个小的 nginx 健康检查包:

sudo systemctl status nginx --no-pager -l
sudo nginx -t
sudo journalctl -u nginx --since '-30 minutes' --no-pager
  • 如果 systemctl 显示 active (running),这只证明服务进程存在
  • nginx -t 告诉你配置是否有效
  • journalctl 显示最近的重新加载是否失败、证书文件是否丢失或虚拟主机在启动时是否出现问题。

对于 Apache 用户,等效的语法检查是 apachectl configtest。一旦你知道有东西在监听,下一个证明更具体:当你从等式中移除外部网络时,正确的站点是否在本地响应?

💡 提示:首先测试配置,然后在适当时优先使用 reload 而不是盲目重启。重新加载会验证新配置,如果新配置不好则保持旧工作进程;盲目重启在事件中期要粗糙得多。

第 4 步:使用正确的主机和 SNI 在本地测试站点

这是整个调查中最重要的分支。普通的 curl 127.0.0.1 可能会产生误导。许多服务器托管多个站点,并根据 Host 头或 HTTPS 的 SNI 选择响应。您不是在询问本地是否有某些响应。您是在询问正确的站点路径是否在本地响应。

使用保留主机名逻辑的本地测试:

curl -I http://127.0.0.1/ -H 'Host: example.com'
curl -v --resolve example.com:443:127.0.0.1 https://example.com/

# Only as a one-off diagnostic if you already know the cert is bad:
curl -vk --resolve example.com:443:127.0.0.1 https://example.com/

有意义的成功是来自正确站点的预期页面、预期重定向或预期应用程序响应。它不是默认的 nginx 主机、错误的证书或通用的”它返回了 HTML”。从这里开始有三个清晰的结果:本地成功、本地错误站点或默认证书行为,或本地失败/超时。良好的本地结果指向防火墙、提供商、CDN 或路由检查。不良的本地结果将您留在堆栈内部,在上游或 TLS 分支中。

第5步:如果本地工作但外部不工作,追踪网络路径

一旦本地测试良好,暂时停止对nginx的怀疑。站点堆栈可能在服务器上正常运行,缺失的部分通常位于访问者和该工作本地服务之间的某个地方。从主机防火墙开始,因为它是你控制的最接近的外部边界。

使用你的系统实际使用的工具检查主机端规则,并进行一次快速的自我检查以查找自己造成的阻止:

sudo nft list ruleset

# Or, on systems still using iptables directly:
sudo iptables-save
sudo ip6tables-save

# Fast sanity check for self-inflicted blocking:
sudo fail2ban-client status

该输出仅显示客户操作系统层。它显示提供商端过滤、安全组或存在于VPS外部的面板端防火墙规则。例如,在AlexHost VPS上,机器防火墙和任何面板级网络控制是独立的问题。当本地测试工作但公共访问者仍然失败时,两者都很重要。

⚠️ 警告:如果Docker在主机上发布端口,不要假设UFW输出讲述了整个故事。Docker可以在UFW的常规链之前通过NAT路由已发布的容器流量。这意味着”UFW看起来没问题”并不总是意味着数据包路径没问题。

CDN和负载均衡器也值得在这里有自己的分支。源可能是健康的,但仍然无法直接访问,因为只允许边缘IP范围与其通信。当你需要证明数据包是否到达时,使用tcpdump作为是或否工具:

📝 注意:CDN允许列表后面的失败直接源测试通常指向边缘策略,而不是死源。在该设置中,源被设计为信任CDN路径,而不是每个直接访问者。

sudo tcpdump -ni any 'tcp port 80 or tcp port 443'

如果根本看不到SYN数据包,流量没有到达服务器。如果SYN到达但没有SYN-ACK离开,服务器或防火墙路径仍在阻止握手。如果这两种模式似乎都不是阻止者,剩余的失败通常位于前端上游握手后面。

第 6 步:如果前端有响应但网站仍然损坏,跟踪上游

在这个分支中,Web 服务器可以到达,但其后面的下一跳不够健康,无法完成请求。这里”上游”是指 nginx 接下来将请求交给的服务:应用进程、套接字支持的运行时、容器或其他内部依赖项。

静态页面正常工作,而登录、搜索、结账或 API 路由失败,这是一个强烈的线索,表明前端存在,故障从其后面的交接处开始。

📝 注意:502 视为”下一跳响应不当”,将 504 视为”下一跳响应太慢”。两者都是跟踪上游路径而不是在前端停止的迹象。

检查活跃的交接,然后直接测试上游:

sudo nginx -T

# Direct HTTP upstream example
curl -i http://127.0.0.1:3000/

# Unix-socket-backed HTTP example
curl --unix-socket /run/app.sock http://localhost/

在 nginx 输出中,查找 proxy_passfastcgi_passuwsgi_pass 等指令。您正在检查 nginx 是否指向正确的目标、通过正确的协议、在正确的端口或套接字上。如果涉及容器,请添加一个简短的容器健康检查,而不是猜测:

docker ps
docker logs --tail 50 <container_name>
docker inspect --format '{{json .State.Health}}' <container_name>
docker port <container_name>

如果直接应用测试失败,问题就在 Web 服务器后面。如果它直接工作但通过 nginx 失败,交接配置是要检查的分支。数据库可达性在这里仅作为依赖项检查很重要,而不是作为单独的深入研究。如果这种上游故障模式不断重复,那就是切换到专门的故障排除指南的合适时机,而不是将一个事件拉长成猜测。

第 7 步:隔离 TLS 和证书故障

这个分支更窄:有东西在 443 上应答,但浏览器仍然无法完成干净、可信的 HTTPS 会话。成功的 TCP 连接到端口 443 并不能证明证书、主机名匹配或握手路径是健康的。

检查实际提供的证书:

openssl s_client -connect example.com:443 -servername example.com -verify_hostname example.com -brief

这是你捕捉常见故障形状的地方:错误的主机名、过期的证书、不完整的链或从未完全完成的续期。简单来说,SNI 告诉服务器你指的是哪个主机名。-verify_hostname 检查它提供的证书是否与该主机名匹配。恢复后,验证续期路径,以便这不会成为下一次中断:

⚠️ 警告:如果你依赖 HTTP-01 验证进行证书续期,入站端口 80 必须可达。防火墙或提供商规则阻止 80 可能会在用户报告 HTTPS 看起来已死之前很久就悄悄破坏续期。

sudo certbot renew --dry-run

第8步:在确定为随机故障前检查资源压力

某些事件根本不是可达性故障。路径在技术上是完整的,但服务器资源严重不足、被阻止或过载,无法及时响应。这时网站从某个角度看可能”部分活跃”,但对用户来说仍然是死的。

运行一个小的初步资源检查包:

df -h
df -i
free -h
uptime
vmstat 1 5
sudo journalctl -k -g 'oom|out of memory|killed process'

按模式而不是孤立地读取结果。

  • df -h 显示普通磁盘耗尽。
  • df -i 捕获inode耗尽,此时空间似乎存在但文件系统无法创建更多条目。
  • free -h 在可用内存崩溃且交换活动增加时最重要。
  • uptime 可以显示高负载,即使CPU未达到峰值,这通常意味着任务在等待磁盘或内存压力而不是活跃计算。
  • 关于OOM事件的内核日志行告诉你系统是否开始杀死进程以生存。

提供商图表可以确认时间线。在AlexHost VPS上,它们可用于检查RAM、磁盘或I/O的峰值是否与故障时间一致。但终端证据仍应主导诊断。本节不是调优指南;它是告诉你网站可能在压力下失败而不是路由失败的分支。

分层思考,不要惊慌

end

当 SSH 正常工作但网站无法打开时,保持链路简短且可重复:

  1. 从外部重现故障
  2. 识别最后一个成功的阶段
  3. 确认 DNS 和目标地址
  4. 验证 80/443 上是否有真实监听器
  5. 在本地测试正确的站点
  6. 分支到网络路径、上游、TLS 或资源

💡 提示:在确认新登录仍然有效且仍然有备用访问路径(如提供商控制台访问)之前,不要关闭最后一个正常工作的 SSH 会话。在实时事件中,保持控制权与修复第一个症状一样重要。

保持习惯轻量:从外部监控、保留日志,并使用 certbot renew –dry-run 测试证书。使用备份和控制台路径保护访问。提供商工具——防火墙、图表、控制台(包括 AlexHost)——应该支持故障排除,而不是替代它。专注于修复第一个损坏的层,这样每个事件都能以更清晰的证据和更少的惊慌来处理。