故障排查

12 分钟阅读 · 22 组命令示例

网络调试指南:curl / DNS / 代理 / SSH

适用场景:调试 API 请求、排查 DNS 问题、配置代理、SSH 连接远程服务器。

适用版本:curl 7.82+、OpenSSH 8+、PowerShell 7+;Windows PowerShell 5.1 差异单独标注。

最后核对:2026-07-30

30 秒快速路径

bash
curl --fail-with-body --connect-timeout 5 https://api.example.com/health
nslookup example.com
ssh -v user@example.com

目录

  1. curl — HTTP 请求调试
  2. DNS 排查
  3. 连通性测试
  4. 代理配置
  5. SSH 基础
  6. 常见网络问题排查

curl — HTTP 请求调试

curl 是命令行 HTTP 客户端,调试 API 必备。

基础请求

bash
# GET 请求
curl https://api.example.com/users
curl -sS https://api.example.com/users         # 静默进度条,但仍显示错误

# 只看响应头
curl -I https://example.com                    # HEAD 请求
curl -i https://example.com                    # 响应头 + body 一起看

# 只看 HTTP 状态码
curl -sS -o /dev/null -w "%{http_code}\n" https://example.com
# 输出:200

# 让 HTTP 4xx/5xx 返回非零退出码,同时保留错误正文
curl --fail-with-body https://api.example.com/users

# 查看详细的请求/响应过程
curl -v https://example.com                    # 显示 TLS 握手、请求头、响应头等

# 跟随重定向
curl -L https://short.url/abc                  # -L = follow redirects

# 超时设置
curl --connect-timeout 5 --max-time 10 https://slow-api.com

# 对 curl 判断为临时故障的请求最多重试 3 次,并限制整体重试时间
curl --retry 3 --retry-max-time 20 https://api.example.com/health

--retry-all-errors 会扩大重试范围,但配合管道、重定向或非幂等请求可能造成重复数据或重复操作,不应作为默认模板。

POST / PUT / DELETE

bash
# POST JSON(curl 7.82+;自动设置 Content-Type 与 Accept)
curl --json '{"name":"Example User","email":"user@example.com"}' \
  https://api.example.com/users

# 从文件读取 JSON
curl --json @request.json https://api.example.com/users

# POST 表单数据
curl https://example.com/login \
  --data-urlencode "username=example-user" \
  --data-urlencode "password=REPLACE_ME"

# PUT
curl -X PUT https://api.example.com/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Updated"}'

# DELETE
curl -X DELETE https://api.example.com/users/1

# 上传文件
curl -X POST https://api.example.com/upload \
  -F "file=@./photo.jpg"

请求头与认证

bash
# 不要把真实 Token、密码或 Cookie 写进会被保存的命令历史。
# 以下值仅为占位符;长期凭据应使用权限受控的配置文件或密钥管理器。

# 自定义请求头
curl -H "Authorization: Bearer REPLACE_WITH_TOKEN" \
     -H "Accept: application/json" \
     https://api.example.com/me

# Basic Auth:只写用户名,让 curl 交互提示密码
curl -u username https://api.example.com/admin

# Cookie
curl -b "session=abc123" https://example.com/dashboard
curl -c cookies.txt https://example.com/login     # 保存 cookie
chmod 600 cookies.txt                              # Cookie 文件按敏感信息保护
curl -b cookies.txt https://example.com/dashboard # 使用 cookie

输出与保存

bash
# 保存到文件
curl -o output.html https://example.com          # 自定义文件名
curl -O https://example.com/file.zip             # 用 URL 中的文件名

# 格式化 JSON(配合 jq)
curl -s https://api.example.com/users | jq .
curl -s https://api.example.com/users | jq '.[0].name'

# 测量响应时间
curl -s -o /dev/null -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" https://example.com

PowerShell 的 curl

powershell
# Windows PowerShell 5.1 中 curl 是 Invoke-WebRequest 的别名;
# PowerShell 7+ 通常没有该别名。需要原生 curl 时显式使用 curl.exe

curl.exe -s https://api.example.com/users

# 或者用 PowerShell 原生:
Invoke-RestMethod -Uri "https://api.example.com/users" -Method Get
Invoke-RestMethod -Uri "https://api.example.com/users" -Method Post -Body '{"name":"Example User"}' -ContentType "application/json"

# 只看头
Invoke-WebRequest -Uri "https://example.com" -Method Head | Select-Object StatusCode, Headers

DNS 排查

查询 DNS 解析

bash
# nslookup(所有平台)
nslookup example.com
nslookup example.com 8.8.8.8           # 指定 DNS 服务器

# dig(Linux/macOS,更详细)
dig example.com
dig example.com +short                  # 只看 IP
dig example.com MX                      # 查 MX 记录
dig example.com NS                      # 查 NS 记录
dig @8.8.8.8 example.com               # 用 Google DNS 查

# host(Linux 简洁工具)
host example.com

常见 DNS 问题

bash
# 清除本地 DNS 缓存
# Windows:
ipconfig /flushdns

# macOS:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

# Linux:命令取决于实际启用的解析服务
sudo resolvectl flush-caches               # systemd-resolved
sudo systemctl restart NetworkManager      # 使用 NetworkManager 时的另一种方式

hosts 文件

手动把域名指向特定 IP(优先级高于 DNS):

bash
# 文件位置:
# Windows: C:\Windows\System32\drivers\etc\hosts
# Linux/macOS: /etc/hosts

# 格式:IP 域名
127.0.0.1   myapp.local
127.0.0.1   api.myapp.local
# 用途:本地开发时模拟域名

连通性测试

bash
# ping — 测试 ICMP;失败也可能只是对方或防火墙屏蔽 ICMP
ping google.com                # 持续 ping
ping -c 4 google.com           # 只 ping 4 次(Linux/macOS)
ping -n 4 google.com           # Windows

# traceroute — 追踪路由路径
traceroute google.com          # Linux/macOS
tracert google.com             # Windows

# telnet — 测试 TCP 端口
telnet example.com 443

# nc (netcat) — 更好用
nc -zv example.com 443        # 测试端口
for port in 80 443 8080; do nc -zv example.com "$port"; done

代理配置

终端代理设置

bash
# Linux/macOS — 当前会话
export http_proxy="http://127.0.0.1:7890"
export https_proxy="http://127.0.0.1:7890"
export no_proxy="localhost,127.0.0.1,.local"

# 取消
unset http_proxy https_proxy no_proxy

# 写入 ~/.bashrc 持久化(如果代理一直开着)
powershell
# PowerShell
$env:http_proxy = "http://127.0.0.1:7890"
$env:https_proxy = "http://127.0.0.1:7890"

# 取消
Remove-Item Env:http_proxy, Env:https_proxy -ErrorAction SilentlyContinue

Git 代理

bash
# 给所有仓库设置代理前先确认这是你的长期需求
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890

# 只给 GitHub 设代理
git config --global http.https://github.com.proxy http://127.0.0.1:7890

# 取消
git config --global --unset http.proxy
git config --global --unset https.proxy

npm 代理

bash
npm config set proxy http://127.0.0.1:7890
npm config set https-proxy http://127.0.0.1:7890

# 取消
npm config delete proxy
npm config delete https-proxy

SSH 基础

生成密钥

bash
# 生成 Ed25519 密钥(推荐)
ssh-keygen -t ed25519 -C "your_email@example.com"
# 生成文件:~/.ssh/id_ed25519(私钥)和 ~/.ssh/id_ed25519.pub(公钥)
chmod 600 ~/.ssh/id_ed25519

# 查看公钥(复制到 GitHub/服务器)
cat ~/.ssh/id_ed25519.pub

连接远程

bash
ssh user@hostname              # 基础连接
ssh -p 2222 user@hostname      # 指定端口
ssh -i ~/.ssh/mykey user@host  # 指定密钥文件

首次连接前应通过可信渠道核对服务器主机密钥指纹,不要在不确认的情况下接受发生变化的 host key。

SSH Config(免记 IP 和参数)

bash
# ~/.ssh/config

Host myserver
  HostName 192.168.1.100
  User deploy
  Port 22
  IdentityFile ~/.ssh/id_ed25519

Host github
  HostName github.com
  User git
  IdentityFile ~/.ssh/github_key

# 使用:
ssh myserver                   # 等价于 ssh -i ... deploy@192.168.1.100
git clone github:user/repo    # 使用 github 配置

端口转发(隧道)

bash
# 本地转发:把远程端口映射到本地
ssh -o ExitOnForwardFailure=yes -L 127.0.0.1:8080:127.0.0.1:3000 user@server
# 访问本地 8080 → 实际访问 server 上的 3000

# 远程转发:把本地端口暴露到远程
ssh -o ExitOnForwardFailure=yes -R 127.0.0.1:9090:127.0.0.1:4321 user@server
# 默认只允许远程服务器本机访问 127.0.0.1:9090;
# 是否允许其他主机访问取决于 sshd 的 GatewayPorts 配置

# 后台运行隧道
ssh -fN -o ExitOnForwardFailure=yes -L 127.0.0.1:8080:127.0.0.1:3000 user@server
# -f 后台运行  -N 不开 shell

文件传输

bash
# scp — 简单拷贝
scp file.txt user@server:/home/user/       # 上传
scp user@server:/var/log/app.log ./        # 下载
scp -r ./dist user@server:/var/www/        # 上传目录

# rsync — 增量同步(更快)
rsync -avz ./dist/ user@server:/var/www/   # 上传(只传有变化的)
rsync -avzn --delete ./dist/ user@server:/var/www/ # 先 dry-run
rsync -avz --delete ./dist/ user@server:/var/www/  # 确认后执行;会删除远端多余文件

rsync 源路径末尾的 / 会改变复制层级;使用 --delete 前必须核对源、目标和 dry-run 输出。


常见网络问题排查

API 请求失败排查流程

bash
# 1. 确认域名能解析
nslookup api.example.com

# 2. 辅助检查 ICMP;ping 失败不能单独证明服务不可达
ping api.example.com

# 3. 确认端口通
nc -zv api.example.com 443

# 4. 确认 HTTP 层
curl -v https://api.example.com/health

# 5. 确认不是代理问题
curl --noproxy '*' https://api.example.com/health

CORS 问题

bash
# 模拟带 Origin 的实际 GET
curl -i -H "Origin: http://localhost:3000" https://api.example.com/data

# 模拟浏览器 OPTIONS 预检
curl -i -X OPTIONS \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: POST" \
  https://api.example.com/data
# 检查 Allow-Origin、Allow-Methods、Allow-Headers 和凭据策略

SSL/TLS 证书问题

bash
# 查看证书信息
curl -vI https://example.com 2>&1 | grep -A 5 "Server certificate"

# 跳过证书验证(仅调试用!)
curl -k https://self-signed.example.com

# 查看证书详情
openssl s_client -connect example.com:443 -servername example.com </dev/null

慢请求定位

bash
curl -s -o /dev/null -w "\
DNS查询:    %{time_namelookup}s\n\
TCP连接:    %{time_connect}s\n\
TLS握手:    %{time_appconnect}s\n\
首字节(TTFB): %{time_starttransfer}s\n\
总时间:      %{time_total}s\n" https://example.com

官方参考