返回首页

Nginx 日志分析与 4xx/5xx/超时故障排查实战

📅 创建于 2026-06-16 🔄 更新于 2026-06-16 📝 2013 字

Nginx 日志分析与 4xx/5xx/超时故障排查实战

来源:运维派 | 发布日期:2026-06-13

Nginx 是绝大多数 Web 业务的入口。日志字段非常规范,定位问题就是"按图索骥"——搞清楚日志字段含义、状态码语义、超时参数,80% 的故障在日志里直接可见。

一、access_log 字段详解

完整字段说明(17 个字段)

推荐生产日志格式:

log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent '
                '"$http_referer" "$http_user_agent" '
                'rt=$request_time uct="$upstream_connect_time" '
                'uht="$upstream_header_time" urt="$upstream_response_time" '
                'req_id=$request_id '
                'fwd="$http_x_forwarded_for" scheme="$scheme" '
                'host="$host" uri="$request_uri"';
字段 含义 正常范围 异常判断
$remote_addr 客户端 IP(反代后配合 $http_x_forwarded_for 看真实 IP) 突变的 IP 分布可能提示攻击
$remote_user HTTP Basic Auth 用户名 异常用户登录
$time_local 服务器本地时间 [12/Jun/2026:10:00:00 +0800] 时区是否对齐
$request 完整请求行(方法 + URI + 协议) 异常方法/路径
$status HTTP 状态码 200/301/304 4xx/5xx 突增即问题
$body_bytes_sent 响应体字节数(不含 header) 0 字节可能有问题
$http_referer Referer 头 排查盗链/爬虫来源
$http_user_agent 客户端 UA 可疑 UA 识别爬虫/攻击
$request_time 请求处理总耗时(秒),从 Nginx 收到首字节到发完末字节 0.001~0.500 >1.0 为慢请求
$upstream_connect_time 与上游建立 TCP 连接的时间(秒) 0.000~0.005 >0.050 怀疑网络问题
$upstream_header_time 连接 + 收到上游首字节的时间 ≤ upstream_response_time 用于定位上游慢在哪(建立连接慢 vs 响应慢)
$upstream_response_time 与上游交互总时间 应 ≤ $request_time 若 >> request_time 检查 Nginx 自身处理
$request_id Nginx 生成唯一 ID(1.11+) 串联整个调用链 跨组件 trace 的关键
$http_x_forwarded_for 客户端真实 IP(经过反代后) 多级反代时逗号分隔
$scheme HTTP 或 HTTPS 检查协议一致性
$host 请求的 Host 头 排查虚拟主机路由
$request_uri 完整 URL(含 query string) 对比 $request 排查 rewrite

核心排查三件套: $request_time$upstream_connect_time$upstream_response_time 三个字段一起看。若 $request_time ≈ $upstream_response_time,瓶颈在上游;若 $request_time >> $upstream_response_time,卡在 Nginx 自身或客户端侧。

error_log 关键字速查

error_log 关键字 含义 定位方向
connect() failed 连不上上游 上游进程/防火墙/端口/IP 配置
upstream timed out 上游超时 上游响应慢/proxy_read_timeout 太短
no live upstreams 所有上游不可用 全部被 max_fails 标记为 down
worker connections are not enough 连接数耗尽 调大 worker_connections
accept() failed (24: Too many open files) 文件描述符耗尽 worker_rlimit_nofile
client sent invalid header line 客户端非法 header 检查客户端版本/行为
SSL_do_handshake() SSL 握手失败 证书/协议兼容性
limiting requests 触发限流 limit_req/limit_conn 配置
upstream sent too big header 上游响应头超出 proxy_buffer_size 调大缓冲区
cache lock 缓存锁竞争 检查缓存配置

二、超时配置全景

Nginx 超时配置分布在 客户端、Nginx 自身、上游、FastCGI 四层:

客户端层

client_body_timeout 30s;        # 客户端发送请求体的超时(空闲超时)
client_header_timeout 30s;      # 客户端发送请求头的超时(空闲超时)
send_timeout 30s;               # Nginx 向客户端发送响应的超时(两次写入之间)
keepalive_timeout 65s;          # 长连接保持时间
keepalive_requests 100;         # 长连接最多处理多少请求后断开
client_max_body_size 50m;       # 客户端请求体最大
client_body_buffer_size 128k;   # 请求体缓冲区
client_header_buffer_size 1k;   # 请求头缓冲区
large_client_header_buffers 4 8k;  # 大请求头缓冲区

上游层

upstream backend {
    server 10.0.2.10:8080 max_fails=3 fail_timeout=30s;
    server 10.0.2.11:8080 max_fails=3 fail_timeout=30s;
    keepalive 64;
    keepalive_requests 1000;
    keepalive_timeout 60s;
}

location / {
    proxy_connect_timeout 5s;        # TCP 三次握手总时长(生产 1~5s)
    proxy_send_timeout 60s;          # 向上游发送请求的"两次写操作之间"超时
    proxy_read_timeout 60s;          # 等待上游响应的"两次读操作之间"超时
    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 3;
    proxy_next_upstream_timeout 30s;
}

⚠️ proxy_next_upstream 对非幂等接口(POST/PUT)默认不应启用重试,会放大故障。

FastCGI 层

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php-fpm.sock;
    fastcgi_connect_timeout 5s;
    fastcgi_send_timeout 60s;
    fastcgi_read_timeout 60s;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
}

三、4xx 状态码排查(10 种)

状态码 含义 常见根因 排查命令
400 Bad Request 请求头格式错、body 超 client_max_body_size、URL 编码非法、HTTP/0.9 被拒绝 awk '$9 == 400 {print $7}' access.log \| sort \| uniq -c \| sort -rn \| head -20
401 Unauthorized 没带/格式错误的 Authorization 头、token 过期、Basic Auth 错 awk '$9 == 401 {print $7}' access.log \| sort \| uniq -c
403 Forbidden IP 黑名单 deny、文件权限、SELinux、目录列表关闭、限流触发 grep -i 'denied\|permission denied' error.loggrep 'avc denied' /var/log/audit/audit.log
404 Not Found 路径打错、静态文件不存在、反代后端无此路径、try_files 配错、反代没传 $request_uri awk '$9 == 404 {print $7}' access.log \| sort \| uniq -c \| sort -rn \| head -20
405 Method Not Allowed POST 请求静态文件、反代上游只支持 GET、limit_except 限制 检查请求方法与 location 配置
408 Request Timeout 客户端慢请求、慢请求体、keepalive 客户端长时间不发请求 grep -i 'client timed out' error.log
413 Payload Too Large 请求体超过 client_max_body_size 调大 client_max_body_size(文件上传业务常见)
414 URI Too Long URL 超 large_client_header_buffers 总容量 调大缓冲区(注意调太大可被攻击)
429 Too Many Requests limit_req/limit_conn 触发 grep -i 'limiting requests' error.log
499 Client Closed Request Nginx 特有(非 HTTP 标准),客户端主动断开连接 结合 $request_time 看慢请求导致客户端不耐

499 突然飙升:通常是慢请求导致客户端超时主动断开,需排查上游延迟。

四、5xx 状态码排查

状态码 含义 常见根因 排查方向
500 Internal Server Error 上游应用抛异常、if 指令语法错误、模块加载失败 grep '\[error\]' error.log \| tail -50
502 Bad Gateway 上游未启动/防火墙挡/IP 端口错、accept 队列满、磁盘满、连接数打满 grep -i 'connect() failed' error.log
503 Service Unavailable 所有上游被 max_fails 标为 down、limit_req/limit_conn 触发 awk '$9 == 503 {print $7}' access.log
504 Gateway Timeout 上游处理慢导致 proxy_read_timeout 超时 awk '$9 == 504 {print $14, $18}' access.log

502 vs 504 区别

维度 502 504
阶段 握手阶段失败或上游主动断开 握手成功但上游迟迟不响应
$upstream_connect_time 经常为 -(没连上) 正常值(数毫秒)
$upstream_response_time 无(连不上所以没响应) 等于 proxy_read_timeout 配置值
error_log 关键字 connect() failedno live upstreams upstream timed out

五、真实案例复盘

案例 1:上游 Tomcat 慢查询导致 504

现象:每分钟 200~300 个 504,业务反馈部分用户加载缓慢。

排查

# 发现 504 的 request_time 都集中在 60 秒
awk '$9 == 504 {print $14}' access.log | sort -n | tail
# 输出:59.998, 60.001, 60.003... 等于 proxy_read_timeout

# 看上游响应时间
awk '$9 == 504 {print $18}' access.log | sort -n | tail

根因:Tomcat 端某个慢查询接口 P99 超过 60 秒,Nginx 配置 proxy_read_timeout 60s 直接切断。

修复

  • 短期:proxy_read_timeout 调到 120 秒,先恢复业务
  • 中期:Tomcat 端优化慢 SQL,加索引、改查询逻辑
  • 长期:上游引入熔断机制,慢请求主动 reject 返回 503 而不是拖到超时

案例 2:上游 accept 队列满导致 502

现象:高并发场景 502 集中在某个 Tomcat 节点。

排查

# Nginx 上看 502 集中在哪个 upstream 地址
awk '$9 == 502 {print $1, $7, $NF}' access.log | sort | uniq -c | sort -rn | head

# Tomcat 侧检查 accept 队列
netstat -s | grep -i listen
# 大量 "SYNs to LISTEN sockets dropped"

根因:Tomcat 接收请求速度跟不上,连接被 Linux 内核丢弃。

修复:调大 Tomcat acceptCount + Linux somaxconn + tcp_max_syn_backlog

案例 3:worker_connections 耗尽

现象:高峰期出现 502,error.log 大量 "worker connections are not enough"。

根因:Nginx worker 进程的最大连接数耗尽。Nginx 总连接数 = worker_processes * worker_connections / 2(每条连接在上游和客户端各算一个)。

修复

worker_processes auto;
worker_rlimit_nofile 65535;
events {
    worker_connections 40960;
    use epoll;
    multi_accept on;
}

案例 4:keep-alive 没复用导致上游连接池打满

现象:上游连接数爆表,Tomcat 报 "connection pool exhausted"。

根因:Nginx 默认每个请求和上游建立新连接,高并发下把上游连接池打满。

修复

upstream backend {
    keepalive 64;
    keepalive_requests 1000;
    keepalive_timeout 60s;
}
location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

案例 5:磁盘满导致 502

现象:突然所有接口 502,Nginx 进程还在,但所有请求无法正常返回。

排查df -h 看到磁盘 100%。

根因:Nginx 写临时文件失败。

修复

truncate -s 0 /var/log/nginx/access.log
# 配置 logrotate 和磁盘水位监控

案例 6:DNS 解析超时导致 502

现象:上游配置域名(proxy_pass http://backend.example.com:8080),重启后 502。

根因:Nginx 启动时解析一次域名缓存,之后不主动重新解析。域名 IP 变更后仍用旧 IP。

修复(Nginx 1.17+):

resolver 10.0.0.1 valid=30s;

案例 7:proxy_buffer 太小导致响应截断

现象:返回的 JSON/HTML 不完整,状态码 200 但客户端报错。

排查

curl -v http://app.example.com/api/big-data  # 看到响应被截断
grep -i 'upstream sent too big header' error.log

修复:调大 proxy_buffer_size 16k; proxy_buffers 4 64k;

六、日志切割与归档

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    rotate 30
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
    endscript
}

关键:postrotatekill -USR1 通知 Nginx 重新打开日志文件,不能 nginx -s reload(会断连接)。

# 归档与清理
xz /var/log/nginx/access.log-20260612
find /var/log/nginx/ -name "*.gz" -mtime +90 -delete

七、监控与告警

nginx-prometheus-exporter

server {
    listen 127.0.0.1:80;
    location /stub_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}
nginx-prometheus-exporter -nginx.scrape-uri=http://localhost/stub_status

Prometheus 告警规则

groups:
  - name: nginx
    rules:
      - alert: Nginx5xxHigh
        expr: rate(nginx_http_responses_total{status=~"5.."}[5m]) > 10
        for: 5m
        labels: { severity: critical }
      - alert: Nginx4xxHigh
        expr: rate(nginx_http_responses_total{status=~"4.."}[5m]) > 100
        for: 5m
        labels: { severity: warning }
      - alert: NginxUpstreamDown
        expr: nginx_upstream_check_up{status="down"} == 1
        for: 1m
        labels: { severity: critical }

八、access_log 实战分析

# QPS 与带宽
awk '{print $4}' access.log | cut -d: -f1-3 | uniq -c
awk '{sum += $10} END {print sum/1024/1024, "MB"}' access.log

# 状态码分布
awk '{print $9}' access.log | sort | uniq -c | sort -rn
awk '$9 ~ /^4/ {c4++} END {print c4/NR*100"%"}'

# 慢 URL 排行
awk '{print $14, $7}' access.log | sort -rn | head -20

# Top IP / URL
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20
awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -20

# 找爬虫
awk -F'"' '$6 ~ /spider|bot|crawl/i {print $6}' access.log | sort | uniq -c | sort -rn | head

# P99 延迟按 URL 聚合
awk '{
    url=$7; rt=$14
    if (rt ~ /^[0-9.]+$/) {
        sum[url] += rt; cnt[url]++; data[url, cnt[url]] = rt
    }
} END {
    for (url in cnt) {
        n = cnt[url]
        for (i = 1; i <= n; i++) arr[i] = data[url, i]
        for (i = 1; i <= n; i++)
          for (j = i+1; j <= n; j++)
            if (arr[i] > arr[j]) { t = arr[i]; arr[i] = arr[j]; arr[j] = t }
        p99 = arr[int(n*0.99)+1]
        printf "%-40s p99=%.3fs n=%d\n", url, p99, n
    }
}' access.log | sort -k3 -rn | head -20

九、日志分析工具链

工具 特点 适用场景
goaccess 终端实时分析,生成 HTML 报表 单机快速排查
ELK (Elasticsearch+Logstash+Kibana) 全文搜索、可视化 Dashboard 企业级日志平台
PLG (Promtail+Loki+Grafana) 轻量级,与 Prometheus 生态集成 中小规模集群
goaccess -f /var/log/nginx/access.log -o /var/www/report.html

十、Nginx 安全配置

# 隐藏版本
server_tokens off;

# 限制方法
if ($request_method !~ ^(GET|POST|HEAD|OPTIONS)$) { return 405; }

# CC 防护
limit_req_zone $binary_remote_addr zone=cc:10m rate=10r/s;
location / { limit_req zone=cc burst=20 nodelay; }

# Slowloris 防护
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;
keepalive_timeout 5s;

限制恶意 User-Agent

map $http_user_agent $bad_ua {
    default 0;
    ~*sqlmap 1;
    ~*nmap 1;
}
if ($bad_ua) { return 403; }

限制 Referer 防盗链

valid_referers none blocked server_names *.example.com;
if ($invalid_referer) { return 403; }

十一、Nginx 性能调优

系统级内核参数

# /etc/sysctl.d/99-nginx.conf
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 300000
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535

Nginx 配置优化

# CPU 亲和性
worker_cpu_affinity auto;

# 文件描述符
worker_rlimit_nofile 65535;

# Gzip 压缩(CPU 敏感业务调低级别)
gzip on;
gzip_min_length 1k;
gzip_comp_level 5;
gzip_types text/plain text/css application/javascript application/json application/xml;

# 缓存文件描述符
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

十二、通用配置模板

通用反代模板

upstream backend {
    least_conn;
    server 10.0.2.10:8080 max_fails=3 fail_timeout=30s;
    server 10.0.2.11:8080 max_fails=3 fail_timeout=30s;
    keepalive 64;
}
server {
    listen 80 backlog=2048 deferred reuseport;
    server_name app.example.com;
    access_log /var/log/nginx/app.access.log main;
    error_log /var/log/nginx/app.error.log warn;
    client_max_body_size 50m;
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        proxy_next_upstream error timeout http_502 http_503 http_504;
    }
}

HTTPS 反代模板

server {
    listen 443 ssl http2;
    server_name app.example.com;
    ssl_certificate /etc/nginx/ssl/app.example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/app.example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    location / { proxy_pass http://backend; }
}
server { listen 80; server_name app.example.com; return 301 https://$server_name$request_uri; }

十三、完整排障清单

检查项 命令
日志在哪 /var/log/nginx/access.log / error.log
Nginx 是否运行 systemctl status nginx
配置语法检查 nginx -t
端口监听 ss -ltn \| grep :80
连接数 ss -scurl localhost/nginx_status
最近的 5xx awk '\$9 ~ /^5/ {print \$9, \$7, \$NF}' access.log \| tail
上游是否存活 curl http://10.0.2.10:8080/health
磁盘使用率 df -h
文件描述符 lsof -p \$(pgrep -f nginx) \| wc -l
错误日志关键字 grep -i 'connect() failed\|upstream timed out' error.log
最近 5 分钟 5xx 错误率 grep '5[0-9][0-9]' access.log \| wc -l / wc -l access.log

十四、常见误区

  • 调大 worker_connections 就能解决性能问题 — 还受 fd、CPU、磁盘、内存影响
  • keepalive 64 越大越好 — 占内存,需按上游连接池容量调
  • proxy_read_timeout 越大越安全 — 上游慢时 Nginx 一直挂着连接,拖累连接数
  • 503 一定是上游挂了 — 也可能是 limit_reqlimit_conn 触发

关联页面

页面关联点
nginx-log-analysis-monitoring-guideNginx 日志分析与监控体系构建指南
nginx-config-pitfallsNginx 典型配置错误复盘
nginx-502-504-connection-reset-guideNginx 502/504/Connection Reset 深度排查
nginx-production-performance-optimization生产级 Nginx 性能优化
nginx-troubleshooting-methodology-8-steps一套可以在生产环境照搬执行的 Nginx 故障排查套路,覆盖 502/504/499/500/con
nginx-security-config-guideNginx 安全配置实战