使用 curl 排查 Nginx、PM2 与 Express 请求问题
本章面向第一次在 Ubuntu 上排查 Node.js 接口问题的开发者。我们从一个常见现象出发,学习如何用几条命令逐层缩小问题范围:
curl http://127.0.0.1:8080/api/user
能够正常返回,但是:
curl https://www.example.com/api/user
却返回 404 Not Found。
学完本章后,你应该能够判断:Express 是否正常、PM2 是否正常管理进程、请求是否到达 Nginx,以及 404、502 更可能来自哪一层。
域名、端口和 PM2 应用名都是示例,请替换为服务器的真实值。
1. 先理解请求链路
通过域名访问接口时,请求通常经过:
curl 或浏览器
↓
DNS:把域名解析成服务器 IP
↓
Nginx:监听 80/443,选择 server 和 location
↓
Express:监听 127.0.0.1:8080,处理 API 路由
↑
PM2:启动和管理 Node.js 进程
PM2 不处理 HTTP 请求。它负责在后台启动 Node.js、查看进程和日志、在进程异常退出后重启。真正处理 HTTP 请求的是 Nginx 和 Express。
下面两条命令走的链路不同:
curl http://127.0.0.1:8080/api/user
curl → Express
而:
curl https://www.example.com/api/user
curl → DNS → Nginx → Express
所以,第一条命令正常只能证明 Express 能处理这个请求,不能证明域名、HTTPS 和 Nginx 都正常。
2. 从最基础的 curl 开始
curl 是一个命令行网络客户端。在没有浏览器的 Ubuntu 服务器上,可以用它发送 HTTP 请求:
curl https://www.example.com/api/user
默认情况下,curl 主要把响应体打印到终端:
{"code":0,"data":{"id":1,"name":"Alice"}}
只看响应体不足以排错,还需要查看状态码、响应头、连接 IP 和 HTTPS 握手过程。
3. curl -i:查看响应头
curl -i https://www.example.com/api/user
-i 是 --include 的缩写,表示把响应头也包含在输出中:
HTTP/1.1 404 Not Found
Server: nginx
Content-Type: text/html
Content-Length: 153
<html><body>404 Not Found</body></html>
这里可以看到:
HTTP/1.1 404 Not Found:HTTP 状态;Server: nginx:响应经过了 Nginx;Content-Type:响应体类型;- 空行之后:响应体。
不要把 -i 和大写的 -I 混淆:
curl -I https://www.example.com/api/user
-I 会发送 HEAD 请求。接口可能只实现了 GET,所以排查普通 GET 接口时优先使用 -i。
4. curl -v:查看完整通信过程
curl -v https://www.example.com/api/user
-v 是 --verbose 的缩写,表示输出详细过程,包括:
- 域名解析出的地址;
- 实际连接的 IP 和端口;
- TCP 连接;
- HTTPS/TLS 握手;
- curl 发出的请求头;
- 服务器返回的状态和响应头。
详细输出中有三个重要前缀:
* curl 自身的连接、DNS 和 TLS 诊断信息
> curl 发给服务器的请求
< 服务器返回给 curl 的响应
例如:
* Host www.example.com:443 was resolved.
* IPv4: 203.0.113.10
* Trying 203.0.113.10:443...
* Connected to www.example.com (203.0.113.10) port 443
> GET /api/user HTTP/1.1
> Host: www.example.com
> User-Agent: curl/8.5.0
< HTTP/1.1 404 Not Found
< Server: nginx
初学阶段先关注四项:
Trying后面的 IP 是否为预期服务器;>后面的请求方法和路径是否正确;Host是否为预期域名;< HTTP/...后面的状态码是什么。
-v 的诊断通常写入标准错误流,响应体写入标准输出流。因此重定向响应体后,终端上仍可能看到诊断信息,这是正常现象。
5. curl -k:临时跳过证书校验
curl -k https://www.example.com/api/user
-k 是 --insecure 的缩写。它让 curl 暂时跳过 HTTPS 服务器证书的身份校验,例如自签名证书、域名不匹配或证书链不完整。
-k 不会:
- 绕过 Nginx;
- 绕过登录;
- 修改请求 URL;
- 把 404 自动变成 200;
- 证明证书已经配置正确。
HTTPS 通信仍然加密,但 curl 不再可靠确认对方身份。因此 -k 只适合诊断,不应该成为生产调用的长期方案。
可以对比:
curl -v https://www.example.com/api/user
curl -vk https://www.example.com/api/user
如果不加 -k 失败、加上后成功,应该修复证书,而不是永久忽略校验。
6. curl -vk 到底表示什么
短参数可以合并:
curl -v -k https://www.example.com/api/user
等价于:
curl -vk https://www.example.com/api/user
其中:
-v:显示 DNS、连接、TLS、请求和响应的详细过程
-k:本次请求暂时忽略 HTTPS 证书校验错误
-vk 适合第一次观察 HTTPS 请求发生了什么,但不要因为它方便就忽略其中暴露的证书问题。
7. 第一步:绕过 Nginx,直接检查 Express
在 Ubuntu 服务器内部执行:
curl -i http://127.0.0.1:8080/api/user
返回 200
说明 8080 可以连接、Express 正在运行、该 GET 路由存在。问题更可能在 Nginx、域名或 HTTPS 层。
连接被拒绝
curl: (7) Failed to connect to 127.0.0.1 port 8080
优先检查:PM2 中应用是否启动、Express 是否监听 8080、.env 中 PORT 是否改变,以及应用是否反复崩溃。
Express 返回 404
说明请求已经到达 Express,但可能是请求路径、GET/POST 方法、Router 挂载前缀不匹配,或者新代码没有构建和重新加载。
检查端口监听:
sudo ss -lntp | grep ':8080'
看到 127.0.0.1:8080 表示服务只允许本机访问。由同一台机器上的 Nginx 反向代理时,这是合理的选择。
8. 第二步:检查 PM2
查看进程状态
pm2 status
| 状态 | 含义 |
|---|---|
online |
进程当前正在运行 |
stopped |
进程已经停止 |
errored |
进程反复启动失败或进入错误状态 |
online 只表示进程存在,不保证每个接口以及 MySQL、Redis 等依赖都正常。
查看详细信息
pm2 describe nloop
重点检查:
script path:是否为预期的dist/server.js;exec cwd:是否为项目目录;interpreter和node.js version:是否为预期的 NVM Node.js;restart time:是否在短时间内反复重启;uptime:进程已经运行多久。
查看最近日志
pm2 logs nloop --lines 50 --nostream
--lines 50:查看最近 50 行;--nostream:显示后退出,不持续占用终端。
持续观察使用:
pm2 logs nloop
按 Ctrl+C 只退出日志查看,不会停止应用。
如果配置了请求日志,可以一边观察 PM2 日志,一边发送请求。Nginx 收到请求但应用没有对应记录,通常说明请求没有代理到 Express;如果应用没有请求日志,则不能仅凭“没有日志”下结论。
PM2 按 Linux 用户保存进程,下面两条命令可能看到不同列表:
pm2 status
sudo pm2 status
不要混用 root 和普通用户管理同一个应用。
9. 第三步:检查 Nginx
查看 Nginx 状态
sudo systemctl status nginx
常见状态:
active (running):Nginx 正在运行;inactive:Nginx 没有运行;failed:启动失败,需要查看错误信息。
检查配置语法
sudo nginx -t
成功时通常看到:
syntax is ok
test is successful
必须牢记:
nginx -t只检查磁盘配置语法,不会让运行中的 Nginx 使用新配置。
修改配置后通常执行:
sudo nginx -t && sudo systemctl reload nginx
&& 表示只有配置检查成功,才执行 reload。
reload 与 restart
sudo systemctl reload nginx
reload 会让新 worker 使用新配置,并尽量让旧 worker 完成已有请求。修改配置后优先使用它。
sudo systemctl restart nginx
restart 会停止后重新启动 Nginx,可能产生短暂中断。通常在 Nginx 状态异常、reload 无法解决或确实需要完整重启时使用。
10. 第四步:查看 Nginx 日志
查看最近 50 行访问日志:
sudo tail -n 50 /var/log/nginx/access.log
查看最近 50 行错误日志:
sudo tail -n 50 /var/log/nginx/error.log
持续观察访问日志:
sudo tail -f /var/log/nginx/access.log
在另一个终端发送请求:
curl -vk https://www.example.com/api/user
tail -f 会等待新日志,按 Ctrl+C 退出观察。
| 现象 | 初步判断 |
|---|---|
| 当前服务器 access log 有记录 | 请求到达了当前 Nginx |
| 当前服务器没有记录 | DNS 可能指向其他服务器,或请求没有到达这里 |
error log 出现 Connection refused |
Nginx 无法连接 Express 端口 |
| Nginx 和应用都记录了请求 | 请求已经到达 Express |
| Nginx 有记录,应用没有请求记录 | 可能没有进入代理 location,需要结合配置确认 |
如果使用了自定义日志路径,应以实际配置为准。可以打印完整的磁盘配置:
sudo nginx -T
-T 会检查并打印所有 include 合并后的配置,输出可能很长。
11. 初学者需要看懂的 Nginx 配置
一个简化的 HTTPS 反向代理配置是:
server {
listen 443 ssl;
server_name www.example.com;
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
}
| 指令 | 作用 |
|---|---|
listen 443 ssl |
接收 443 端口的 HTTPS 请求 |
server_name |
指定哪些域名使用这个 server 块 |
location /api/ |
匹配以 /api/ 开头的请求 |
proxy_pass |
把请求转发到 Express |
如果 server_name 没有匹配 www.example.com,请求可能进入另一个默认站点。
如果 /api/ 的 location 没有加载,请求可能进入静态页面 location。Nginx 找不到 /api/user 对应的文件时,就会返回 404。
12. proxy_pass 末尾斜杠为什么重要
下面两个配置只差一个 /,但转发路径不同。
保留 /api/ 前缀
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
客户端请求 /api/user,Express 通常仍收到:
/api/user
去掉匹配到的 /api/ 前缀
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}
客户端请求 /api/user,Express 通常收到:
/user
如果 Express 只定义了 /api/user,第二种配置可能让 Express 返回 404。
最简单的验证方式是对比:
curl -i http://127.0.0.1:8080/api/user
curl -vk https://www.example.com/api/user
再结合 Express 请求日志观察它最终收到的 URL。
13. 如何初步判断 404 来自哪里
| 现象 | 更应该先检查 |
|---|---|
| 直接访问 8080 也返回 404 | Express 路由和请求方法 |
| 8080 正常,域名返回 404 | Nginx 的 server、location 和 proxy_pass |
| Nginx 与 Express 都记录了请求 | Express 最终收到的路径 |
| Nginx 有记录,Express 没有请求记录 | 请求可能没有进入 proxy_pass |
| 当前服务器没有 Nginx 访问日志 | DNS、IPv6、CDN 或其他服务器 |
响应中出现:
Server: nginx
只能证明响应经过 Nginx,不能百分之百证明 404 由 Nginx 产生。Express 返回的 404 也可能经 Nginx 转发给客户端。
14. 常见状态码告诉了我们什么
| 状态码 | 常见含义 | 首先检查 |
|---|---|---|
404 |
路径没有匹配 | Nginx location、proxy_pass、Express Router |
502 |
Nginx 无法正常连接上游 | PM2、Express 端口、proxy_pass 地址 |
504 |
Nginx 等待上游超时 | 慢接口、数据库、Nginx 超时配置 |
301/302 |
发生重定向 | 响应中的 Location |
如果 Nginx 已启动但 Express 尚未启动,常见结果是 502 Bad Gateway,而不是 404。Express 后来开始监听后,Nginx 通常会自动恢复连接,不需要为此重启 Nginx。
15. 为什么重启后恢复不等于找到原因
假设:
127.0.0.1:8080/api/user 正常
https://www.example.com/api/user 返回 404
随后执行:
sudo nginx -t && sudo systemctl restart nginx
问题消失。最常见的解释是:
磁盘上的 Nginx 配置已经正确
↓
运行中的 worker 仍使用旧配置
↓
restart 重新读取磁盘配置
↓
请求恢复正常
“重启前没有修改配置”不代表运行中的 Nginx 已经加载当前磁盘配置。配置可能更早就修改过,但一直没有 reload。
重启还会破坏部分故障现场。问题恢复也可能恰好与 DNS 更新或外部依赖恢复同时发生。下次先保存:
curl -vk https://www.example.com/api/user
curl -i http://127.0.0.1:8080/api/user
pm2 status
pm2 logs nloop --lines 50 --nostream
sudo systemctl status nginx
sudo tail -n 50 /var/log/nginx/access.log
sudo tail -n 50 /var/log/nginx/error.log
sudo nginx -t
记录现场后,优先尝试:
sudo nginx -t && sudo systemctl reload nginx
如果 reload 后恢复,就更有理由判断运行配置此前没有更新。
16. 一套可以照着执行的排查流程
第一步:检查 Express
curl -i http://127.0.0.1:8080/api/user
- 正常:继续检查 Nginx;
- 连接失败:检查 PM2、端口和应用日志;
- 返回 404:检查 Express 路由。
第二步:检查 PM2
pm2 status
pm2 describe nloop
pm2 logs nloop --lines 50 --nostream
确认应用在线、入口与 Node.js 版本正确,并且没有反复重启。
第三步:检查 Nginx
sudo systemctl status nginx
sudo nginx -t
确认 Nginx 正在运行,磁盘配置语法正确。
第四步:观察域名请求
curl -vk https://www.example.com/api/user
关注实际连接 IP、请求路径、Host 和响应状态。
第五步:检查日志
sudo tail -n 50 /var/log/nginx/access.log
sudo tail -n 50 /var/log/nginx/error.log
把请求时间与日志时间对应起来。
第六步:确认配置后重新加载
sudo nginx -t && sudo systemctl reload nginx
不要跳过配置检查,也不要把 restart 当成第一个排查动作。
17. 进阶:绕过 DNS 测试本机 Nginx
怀疑域名指向错误服务器时,可以执行:
curl -vk \
--resolve www.example.com:443:127.0.0.1 \
https://www.example.com/api/user
它的含义是:
URL、Host 和 HTTPS 域名仍使用 www.example.com
但是本次不使用公网 DNS 结果
而是强制连接 127.0.0.1:443
它比直接访问:
curl -k https://127.0.0.1/api/user
更适合测试基于域名的 Nginx 配置。直接使用 IP 会让 Host 和 TLS 域名变成 127.0.0.1,可能命中错误的 server。
如果 --resolve 测试正常,而普通域名请求异常,继续检查:
- DNS 的 A 记录;
- DNS 的 AAAA 记录;
- 域名前面的 CDN;
- 是否存在多台服务器。
查看 DNS 地址:
dig +short A www.example.com
dig +short AAAA www.example.com
强制 IPv4 或 IPv6:
curl -4vk https://www.example.com/api/user
curl -6vk https://www.example.com/api/user
如果 IPv4 正常而 IPv6 异常,通常要检查 AAAA 记录及服务器的 IPv6 Nginx 配置。
18. 进阶:让部署脚本识别 HTTP 失败
普通 curl 收到 HTTP 404 时,命令本身仍可能以成功退出码结束,因为网络请求确实完成了。
部署健康检查可以使用:
curl \
--fail-with-body \
--silent \
--show-error \
--connect-timeout 3 \
--max-time 10 \
http://127.0.0.1:8080/health
参数含义:
--fail-with-body:HTTP 400 及以上返回非零退出码,同时保留响应体;--silent:关闭进度条;--show-error:静默模式下仍显示错误;--connect-timeout 3:最多等待 3 秒建立连接;--max-time 10:整个请求最多运行 10 秒。
这是自动部署验证方式,不是第一次人工排查必须掌握的内容。
19. 初学者常见误区
本机 8080 正常,所以 Nginx 一定正常
8080 请求绕过了 Nginx,只能证明 Express 基本正常。
nginx -t 成功,所以新配置已经生效
nginx -t 只检查磁盘文件,还要执行 reload 才能应用。
PM2 显示 online,所以所有接口一定正常
online 只表示进程存在,不代表路由、数据库和业务依赖都正常。
使用 -k 后成功,所以 HTTPS 已修复
-k 只是忽略证书验证,真正的证书问题依然存在。
看到 404 就重启所有服务
重启可能暂时恢复,也可能让错误证据消失。先记录 curl 输出、日志和进程状态。
Server: nginx 说明 404 一定由 Nginx 产生
Express 的响应也可能通过 Nginx 返回,需要结合 Nginx 和应用日志判断。
20. 最后总结
遇到域名访问异常时,先记住这条最短路径:
curl 直接检查 Express
↓
检查 PM2 状态和应用日志
↓
curl -vk 观察域名请求
↓
检查 Nginx 状态、配置和日志
↓
确认配置后执行 reload
常用命令汇总:
curl -i http://127.0.0.1:8080/api/user
curl -vk https://www.example.com/api/user
pm2 status
pm2 describe nloop
pm2 logs nloop --lines 50 --nostream
sudo systemctl status nginx
sudo nginx -t
sudo tail -n 50 /var/log/nginx/access.log
sudo tail -n 50 /var/log/nginx/error.log
sudo nginx -t && sudo systemctl reload nginx
真正有价值的不是记住所有命令,而是每次只验证一层,用结果决定下一步,不用重启代替定位。
练习题
curl http://127.0.0.1:8080/api/user正常,可以证明域名和 Nginx 都正常吗?为什么?curl -v输出中的*、>、<分别表示什么?curl -k会绕过 Nginx 或用户身份认证吗?它真正忽略的是什么?- 为什么执行
sudo nginx -t成功后,新配置仍可能没有生效? - Nginx 返回 502,而直接访问 8080 提示连接被拒绝,应该先检查什么?
- 直接访问
/api/user正常,但经过 Nginx 后 Express 收到/user,应该检查哪个配置细节? - 当前服务器的 Nginx access log 没有请求记录,但域名请求确实返回了页面,可能有哪些原因?
- 为什么问题发生时不建议第一时间重启 Nginx 和 PM2?重启前至少应该保存哪些信息?