有台服务器上的站点突然访问不了。登录上去一查,第一反应是配置文件出了问题, 于是执行了最常见的检查命令:

nginx -t

返回结果是这样的:

nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

配置完全没问题。但站点就是打不开。这次排查绕了几个弯, 记录一下完整过程和几个容易踩的坑。

第一步:确认服务到底在不在跑

「配置检查通过」和「服务正在运行」是两件完全不同的事—— 这是我这次最大的思维惯性错误。nginx -t 只验证语法, 它不关心进程是否存在。

systemctl is-active nginx
systemctl is-enabled nginx

结果很有意思:inactive(没在跑)但 enabled(设置了开机自启)。 说明服务在某次运行中停掉了,之后再没起来。

第二步:确认有没有进程在监听端口

进一步核实,看看 80 和 443 到底有没有人听:

ss -lntp

输出里只有 SSH 和 rpcbind,80 端口空空如也。到这里其实已经定位到问题了: Web 服务根本没启动。

顺手也确认了 Docker 没有在跑、没有别的进程代管这 80 端口, 排除「服务其实由别的方式提供」的可能。

第三步:查日志找原因

这里踩了第一个坑。nginx -V 显示编译前缀是 /usr/local/nginx, 但配置里 error_log 指向的是别处。如果只看 /usr/local/nginx/logs/error.log,看到的会是几年前的古董日志, 完全误导判断。

正确做法是先拿到实际生效的完整配置,再看日志路径:

nginx -T | grep -E "error_log|access_log"

nginx -T 会把所有 include 进来的配置文件全部展开打印出来, 包括每一个 server 块。排查「我改的配置到底生效了没有」这类问题时, 这是最有用的一个命令,没有之一。

第四步:一个把我带偏的假象

这次真正浪费时间的地方不在服务器上,而在本地的测试方法。

我从外部测试端口连通性,结果显示 80 和 443 都是「可以连接」的。 既然端口能连上,我就一度认为服务是活的,问题出在应用层。

但实际用 curl 发请求时,得到的是:

curl: (52) Empty reply from server

连接建立成功,却没有任何响应就直接断开。 为了确认这不是 Nginx 的行为,我做了个对照实验—— 去连一个肯定没有任何程序监听的冷门端口:

nc -z -w 4 <服务器IP> 9876

居然也显示「成功」。

结论:这台服务器网络路径上的中间设备(云厂商的 SYN 代理一类的机制) 会对所有 TCP 连接都先回一个握手成功,因此从外部做端口扫描判断服务是否存活是不可靠的。 真正的判据只有服务器内部的实际监听状态。

这类「假阳性」在排查时非常危险:它会让你一直往错误的方向找问题。 后来我养成了习惯——先在服务器上 curl -I http://127.0.0.1/ 确认服务本身是否正常, 再谈外部能不能访问,两头分开看。

第五步:启动并验证

既然配置语法没问题,直接启动即可:

nginx -t && systemctl start nginx
systemctl is-active nginx

然后在服务器本机验证,这一步不经过任何外部网络:

curl -I -H "Host: example.com" http://127.0.0.1/

确认返回 200 OK 之后,再从外部访问确认。 分层验证的好处是,一旦出问题能立刻判断是「服务本身」还是「网络/中间设备」的问题。

顺手清掉的一个隐患

排查过程中还发现了一个「能通过语法检查的坏配置」:

location / {
    index index.htm index.html index.php
    try_files $uri /index.php$uri;
}

index 指令后面漏了分号。因为 Nginx 会把分号前的内容全部当作参数, 所以它把 try_files 也当成了 index 的一个参数—— 语法上完全合法,检查也不会报错,但行为完全不是你想要的。

这个坑提醒我:nginx -t 通过只代表「语法合法」,不代表「逻辑正确」。 配置改完,一定要实际请求一次,用 curl 看状态码和响应内容。

经验总结

  • 配置语法检查通过 ≠ 服务在运行,这是两件独立的事情;
  • nginx -T 是确认「配置是否真的生效」的首选工具;
  • 日志路径以 nginx -T 打印出来的为准,别猜;
  • 外部端口扫描可能被中间设备干扰,判断服务存活要在服务器本机做;
  • 排查顺序建议固定为:进程 → 监听端口 → 日志 → 本机请求 → 外部请求。

按这个顺序走一遍,大多数「网站打不开」的问题都能在几分钟内定位。 最大的敌人不是问题本身,而是先入为主的假设。