Nginx日志分析命令实践和常见问题排查思路

描述

背景与问题

日常运维工作中,日志分析是排查问题最直接的手段。Nginx 作为入口层代理,几乎所有请求都要经过它。当网站出现响应慢、500 错误、502 网关超时、限流失效等问题时,第一反应应该是查 Nginx 日志。但很多运维人员面对一堆文本日志,不知道从哪下手,看不懂关键字段的含义,导致排查方向错误,白白浪费时间。

本文以 2026 年最新的 Nginx 1.27.x 版本为基准,系统讲解 Nginx 访问日志和错误日志的完整字段含义、日志格式配置、日志分析命令实践、常见问题排查思路,以及生产环境最佳配置建议。读完本文,你能够独立完成日志分析、定位故障根因、输出可执行的优化方案。

1. Nginx 日志基础架构

1.1 日志写入原理

Nginx 采用异步写入机制,日志数据先写入内存缓冲区,等缓冲区写满或者达到刷新时间阈值时才写入磁盘文件。这种设计避免了对每个请求都执行磁盘 I/O,显著提升了性能。但这也带来一个风险:如果 Nginx 异常崩溃,缓冲区中尚未写入磁盘的日志会丢失,这在故障复盘时会造成困扰。

Nginx 日志模块的核心组件包括:

ngx_http_log_module:负责处理访问日志的写入

ngx_errlog_module:负责处理错误日志的写入

log_file:日志写入的目标文件路径

open_log_file_cache:日志文件描述符缓存,避免频繁打开关闭文件

配置文件中的 access_log 指令和 error_log 指令分别控制两类日志的输出目标。

1.2 访问日志与错误日志的区别

访问日志记录每一个请求的详细信息,包括请求来源、请求路径、响应状态码、响应大小、响应时间等。访问日志是分析流量、排查业务异常的基石。

错误日志记录 Nginx 自身运行时的异常情况,包括配置文件语法错误、 upstream 连接失败、权限问题、文件描述符耗尽等。错误日志是排查 Nginx 自身故障的唯一入口。

两个日志文件路径通过不同指令分开配置,职责清晰。在生产环境中,必须确保这两个日志文件都正常写入,且磁盘空间充足。

1.3 默认日志路径

主流 Linux 发行版的 Nginx 默认日志路径如下:

Debian/Ubuntu:/var/log/nginx/access.log 和 /var/log/nginx/error.log

RHEL/CentOS/ AlmaLinux:/var/log/nginx/access.log 和 /var/log/nginx/error.log

Alpine:/var/log/nginx/access.log 和 /var/log/nginx/error.log

源码编译安装的 Nginx 默认在 $prefix/logs/ 目录。如果通过 Docker 容器运行,日志通常挂载到宿主机目录,需要特别确认路径。

2. 访问日志完整字段解析

2.1 默认日志格式

Nginx 默认的 access_log 格式使用预定义的 combined 格式,字段如下:

 

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

 

这个格式输出类似下面的内容:

 

192.168.1.100 - - [14/Apr/202630:45 +0800] "GET /api/users HTTP/1.1" 200 1234 "https://example.com/index.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

 

逐个字段解析如下。

2.2 核心必懂字段

$remote_addr

客户端真实 IP 地址。如果客户端没有使用代理,这个地址就是真实的来源 IP。如果请求经过七层负载均衡或 CDN 到达 Nginx,这个字段显示的是上一跳的 IP 地址,而不是客户端真实 IP。这时需要通过 $http_x_forwarded_for 获取真实来源。

在有代理链路的场景中,$remote_addr 可能显示的是 SLB/Nginx 层地址,造成所有请求都看起来来自同一个 IP 的假象。这对于基于 IP 的限流和访问控制是致命问题,必须通过 set_real_ip_from 指令配合 $remote_addr 修正,或者直接使用 $http_x_forwarded_for 的第一个 IP。

$remote_user

Basic 认证的用户名。如果请求没有经过认证,这个字段为空,用短横线占位。在企业内部系统使用 Basic Auth 时,这个字段才有实际意义。

[$time_local]

本地时间,格式为 [14/Apr/202630:45 +0800]。这个时间是 Nginx 服务器的本地时间,与客户端时间可能存在时区差异。括号必须保留,日志解析工具通常依赖这个时间戳格式做切分。

注意:time_iso8601 不同。$time_iso8601输出 ISO 8601 格式如2026-04-14T1045+08:00`,适合对接结构化日志系统如 ELK。

"$request"

完整的请求行,包含方法、URI、协议版本。格式为 "GET /api/users HTTP/1.1"。这个字段是分析请求路径的核心,可以统计热门页面、发现异常请求模式。

需要注意的是,如果请求包含查询参数,参数会包含在 URI 中,如 GET /search?q=nginx HTTP/1.1。但某些敏感参数值可能被 $http_authorization 过滤。请求方法通过这个字段的第一个单词提取。

$status

HTTP 响应状态码。常见状态码含义如下:

200:请求成功

206:部分内容,用于断点续传或大文件分片下载

301/302:重定向,301 是永久重定向,302 是临时重定向

304:Not Modified,浏览器缓存未更新

400:Bad Request,客户端请求语法错误

401:Unauthorized,需要认证

403:Forbidden,权限不足或资源被禁止访问

404:Not Found,资源不存在

499:Client Closed Request,客户端在服务端响应前关闭连接,常见于 PHP-FPM 处理超时场景

500:Internal Server Error,服务端代码异常

502:Bad Gateway,upstream 错误,上游服务不可用

503:Service Unavailable,服务过载或正在维护

504:Gateway Timeout,upstream 超时,上游响应过慢

在故障排查时,$status 是首要关注指标。出现大量 502/504 说明 upstream 有问题;大量 499 说明客户端等待不及开始关闭连接;大量 400/403 可能是攻击或配置错误。

$body_bytes_sent

发送给客户端的响应体字节数,不包含 HTTP 头部。这个数值不包括 SSL 加密产生的额外字节,对于下载服务,这个字段直接反映实际传输量。结合 $status 可以分析带宽消耗和错误分布。

注意:如果使用 gzip 压缩,gzip_ratio`。

"$http_referer"

Referer 请求头的值,表示当前请求的来源页面。如果用户直接在浏览器输入 URL 访问,则 Referer 为空或短横线。通过搜索引擎访问时,Referer 显示搜索引擎地址。通过外链点击访问时,Referer 显示来源站点。

这个字段对于分析流量来源、追踪推广效果、发现异常外链非常有价值。但 Referer 头可以被客户端伪造或清除,因此仅作为参考,不能作为权威数据源。

"$http_user_agent"

User-Agent 请求头的值,表示客户端类型信息。这个字段内容非常丰富,包括浏览器名称和版本、操作系统、设备类型等。对于 API 请求或非浏览器客户端,这里显示的是调用方的标识信息。

通过分析 $http_user_agent 可以发现恶意爬虫、异常客户端版本分布、APP 兼容性等问题。但 User-Agent 同样可以被客户端任意修改,不能作为安全控制的唯一依据。

2.3 进阶分析字段

$http_x_forwarded_for

当请求经过代理或负载均衡时,真实客户端 IP 会被放在 X-Forwarded-For 请求头中传递。格式为 client_ip, proxy1_ip, proxy2_ip,第一个 IP 是真实来源。如果 Nginx 前面有多级代理,这个字段会包含整条链路的所有 IP。

使用 CDN 或高防 IP 时,这个字段可能被客户端伪造,需要结合 $remote_addr 和 CDN 提供的真实 IP 回源功能做综合判断。

$http_x_real_ip

部分代理(如 Nginx ngx_http_realip_module)在转发请求时,会将真实 IP 放在 X-Real-IP 请求头中。这个字段比 X-Forwarded-For 更简洁,但需要 upstream 服务明确支持。

$request_time

请求处理时间,从接收到请求头到完整响应发送完成的总时间,单位是秒,精确到毫秒。这个时间是 Nginx 内部处理时间与 upstream 响应时间的总和,不包含网络传输时间。

在 2026 年的 Nginx 版本中,默认精度为毫秒级,格式如 0.120。对于 PHP-FPM 或 uwsgi 等动态服务,这个时间包含 PHP/ Python 脚本执行时间;对于静态文件,这个时间主要是磁盘 I/O 和传输时间。

如果 $request_time 异常大,如超过 30 秒,说明 upstream 处理过慢,需要进一步分析慢请求根因。

$upstream_response_time

upstream 服务响应时间,即 Nginx 向后端 upstream 发起请求到收到完整响应的时间。如果请求没有经过 upstream(如直接处理静态文件),这个值为空。

这个字段与 $request_time 的差值,就是 Nginx 自身的处理开销(如等待连接、SSL 握手、过滤处理等)。如果差值过大,说明 Nginx 层存在瓶颈。

$upstream_status

upstream 返回的 HTTP 状态码。如果 Nginx 访问多个 upstream(如 backup 服务器),这个字段记录最后一个成功响应的状态码。通过对比 upstream_status,可以判断错误是发生在 Nginx 层还是 upstream 层。

$upstream_addr

实际处理请求的 upstream 服务器地址,格式为 IP:port。如果是负载均衡场景,这个字段显示实际选中的 upstream 节点。结合 upstream_response_time,可以定位到具体是哪台 upstream 出了问题。

$pipe

如果请求使用了 HTTP pipeline,这个字段为 p,否则为点。HTTP pipeline 在现代 Nginx 中已很少使用,这个字段通常为点。

$request_length

请求的总长度,包括请求行、请求头、请求体。这个指标对于分析上传文件大小、检测异常大请求很有价值。

$msec

Unix 时间戳,精确到毫秒。格式为 1744612245.123。这个时间戳是绝对时间,适合做日志聚合和时间校准,特别是在多台服务器日志汇总分析时,比 $time_local 更可靠。

$connection

连接的序列号,每个连接唯一递增。在排查连接问题时,这个编号用于关联同一连接上的多个请求(同一连接上的多请求会有相同的 connection 编号)。

$connection_requests

当前连接的请求数。HTTP/1.1 协议支持 keep-alive 复用连接,这个数字记录该连接上已处理的请求总数。如果这个数字异常大,可能存在连接泄漏或 keep-alive 配置不当。

$ssl_protocol

SSL/TLS 协议版本,如 TLSv1.2、TLSv1.3。如果连接未使用 SSL,这个字段为空。在 2026 年,TLSv1.2 正在逐步淘汰,主流网站已全面迁移到 TLSv1.3。TLSv1.3 在握手速度和安全性上有显著提升。

$ssl_cipher

当前连接使用的加密套件名称,如 TLS_AES_256_GCM_SHA384。这个字段反映当前 SSL 配置的安全强度。使用弱加密套件会被安全扫描工具标记为漏洞。

$ssl_curves(Nginx 1.27+ 新增)

TLSv1.3 场景下使用的椭圆曲线算法。Nginx 1.27 版本开始在日志中支持记录这个字段,用于分析客户端与服务器协商的加密参数。

$http_cookie

完整的 Cookie 请求头内容。由于 Cookie 通常包含敏感信息,生产环境中不建议将这个字段直接写入日志,除非有明确的脱敏处理流程。

$cookie_*

Nginx 允许通过 $cookie_* 变量提取特定名称的 Cookie 值,如 $cookie_session_id。这种方式比直接记录完整 Cookie 更安全,只记录业务需要的特定字段。

$arg_*

Nginx 允许通过 $arg_* 变量提取 URL 查询参数的值,如 $arg_q。但查询参数中的敏感信息不应写入日志,存在信息泄露风险。

3. 错误日志完整字段解析

3.1 错误日志级别

Nginx 错误日志通过 error_log 指令配置,支持以下级别,按严重程度从低到高排列:

debug:调试信息,仅在编译时加入 --with-debug 参数时可用

info:一般信息

notice:通知

warn:警告,可能存在问题但不影响运行

error:错误,请求处理失败

crit:严重,仅报告严重问题

alert:紧急,必须立即处理

emerg:紧急,系统不可用

生产环境通常设置为 warn 或 error。设置 info 或 debug 会产生大量日志,不仅消耗磁盘空间,还会影响性能。仅在排查问题时临时调整为 debug,问题解决后立即恢复。

3.2 错误日志格式

错误日志没有固定的格式,但通常包含时间戳、错误级别、进程信息、客户端 IP(如果有)、错误描述。示例如下:

 

2026/04/14 1045 [error] 12345#12345: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.100, server: example.com, request: "GET /api HTTP/1.1", upstream: "http://127.0.0.1:8080/api", host: "example.com"

 

这个错误信息表明 Nginx 连接 upstream 失败,原因是 Connection refused。逐段解析:

2026/04/14 1045:错误发生时间

[error]:错误级别

12345#12345:进程 ID,主进程为 12345

*1:连接序列号

connect() failed (111: Connection refused):系统调用失败及 errno

while connecting to upstream:错误发生的上下文

client: 192.168.1.100:客户端 IP

server: example.com:匹配的 server 配置块

request: "GET /api HTTP/1.1":请求详情

upstream: "http://127.0.0.1:8080/api":目标 upstream

host: "example.com":请求的 Host 头

理解这些字段对于定位 upstream 相关错误至关重要。

3.3 常见错误码与含义

111: Connection refused

目标端口没有服务监听。检查 upstream 服务是否启动,端口是否正确绑定。

113: No route to host

网络不可达,可能目标服务器宕机或网络配置错误。

104: Connection reset by peer

对端关闭了连接。可能 upstream 服务异常重启或配置了连接限制。

Connection timed out (110)

连接超时,上游服务响应过慢或网络中存在丢包。

1113: No live upstreams

所有 upstream 都不可用,Nginx 无法找到可用的后端服务器。

2048: Worker process exited with signal 9 (SIGKILL)

工作进程被强制杀死,通常是 OOMKiller 导致。内存泄漏或配置不当是常见原因。

32000: Internal dispatch framework error

Nginx 内部分发框架错误,通常由配置错误或模块冲突引起。

4. 日志格式配置实战

4.1 定义适合分析的日志格式

默认的 combined 格式对生产环境分析而言字段不足够。以下是一个更完整的日志格式配置示例:

 

log_format detailed '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time $upstream_response_time $upstream_status '
                    '$upstream_addr $http_x_forwarded_for '
                    '$ssl_protocol $ssl_cipher '
                    '$connection $connection_requests';

access_log /var/log/nginx/access.log detailed buffer=32k flush=5s;

 

buffer=32k 表示将日志写入 32KB 的内存缓冲区再批量刷盘,减少磁盘 I/O 次数。flush=5s 表示即使缓冲区未满,5 秒后也会强制刷盘。两者配合使用兼顾性能和可靠性。

4.2 条件日志记录

Nginx 支持根据变量条件选择性记录日志。使用 map 指令配合 access_log 的 if 参数,可以排除健康检查请求、静态资源请求,或者只记录慢请求:

 

map $request_uri $log_request {
    ~*^/health$ 0;
    ~*.png$ 0;
    ~*.jpg$ 0;
    default 1;
}

map $request_time $log_slow {
    ~^[0-9]{1,3}.[0-9]{3}$ 0;
    default 1;
}

access_log /var/log/nginx/access.log detailed if=$log_request;

 

这个配置排除了健康检查和图片请求的日志记录,减少无效日志干扰。

4.3 结构化 JSON 日志格式

对接 ELK(Elasticsearch、Logstash、Kibana)或 Loki 等日志收集系统时,JSON 格式日志更易于解析。配置如下:

 

log_format json_log escape=json '{'
    '"time":"$time_iso8601",'
    '"remote_addr":"$remote_addr",'
    '"remote_user":"$remote_user",'
    '"request":"$request",'
    '"status":$status,'
    '"body_bytes_sent":$body_bytes_sent,'
    '"request_time":$request_time,'
    '"upstream_response_time":$upstream_response_time,'
    '"upstream_status":$upstream_status,'
    '"upstream_addr":"$upstream_addr",'
    '"http_referer":"$http_referer",'
    '"http_user_agent":"$http_user_agent",'
    '"http_x_forwarded_for":"$http_x_forwarded_for",'
    '"ssl_protocol":"$ssl_protocol",'
    '"ssl_cipher":"$ssl_cipher",'
    '"connection":"$connection",'
    '"connection_requests":"$connection_requests"'
    '}';

 

注意 $status 和 $body_bytes_sent 等数字类型变量在 JSON 中不能加引号,否则 Elasticsearch 会将其识别为字符串而非数值,失去聚合计算能力。使用 escape=json 参数确保特殊字符被正确转义。

4.4 错误日志配置最佳实践

 

error_log /var/log/nginx/error.log warn;

# 将错误日志同时输出到 syslog,便于集中收集
error_log syslog:server=127.0.0.1:514,facility=local7 warn;

 

将错误日志发送到 syslog 有两个好处:即使 Nginx 日志文件被误删,错误日志仍然保存在 syslog 中;便于集中收集多台服务器的日志进行关联分析。

5. 日志分析命令实战

5.1 基础查看命令

日志文件通常较大,直接用 cat 或 vi 打开会卡死。应使用以下方式分页查看:

 

# 分页查看最后 100 行
tail -n 100 /var/log/nginx/access.log

# 实时追踪新日志(类似 tail -f)
tail -f /var/log/nginx/access.log

# 分页查看从头开始的 100 行
head -n 100 /var/log/nginx/access.log

# 查看特定时间范围的日志
sed -n '/14/Apr/202600/,/14/Apr/202600/p' /var/log/nginx/access.log

 

5.2 状态码统计

统计各状态码的出现次数,按次数降序排列:

 

awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

 

输出示例:

 

15432 200
 3211 304
  1205 404
   312 502
   187 499
    89 500
    12 403

 

从这个输出可以快速判断网站健康度:正常情况下 200 和 304 应占 95% 以上;404 超过 1% 说明存在死链;502/504 出现说明 upstream 有问题;499 出现说明客户端在服务端响应完成前关闭了连接。

5.3 请求时间分析

分析响应时间分布,找出慢请求:

 

# 提取 request_time 并排序,统计慢请求分布
awk '{print $NF}' /var/log/nginx/access.log | awk -F. '{print $1}' | sort | uniq -c | sort -rn | head -20
# 筛选响应时间超过 5 秒的请求
awk '$NF > 5 {print $0}' /var/log/nginx/access.log
# 计算平均响应时间
awk -F'"' '{sum+=$NF; count++} END {print sum/count}' /var/log/nginx/access.log

 

响应时间超过 3 秒的请求应该引起重视,超过 10 秒的请求必须排查。分位数统计比平均值更有意义:如果 P99 是 2 秒,说明 99% 的请求在 2 秒内响应;但如果 P50 是 0.5 秒而 P99 是 10 秒,说明存在少量极端慢请求拖累了整体体验。

5.4 高频请求分析

统计最常访问的 URL(排除静态资源):

 

awk -F'"' '{print $2}' /var/log/nginx/access.log | awk '{print $2}' | grep -v '.(jpg|png|css|js|ico|gif|woff2)' | sort | uniq -c | sort -rn | head -20
# 统计接口调用次数分布,发现异常高频调用
awk -F'"' '{print $2}' /var/log/nginx/access.log | awk '{print $2}' | cut -d'?' -f1 | sort | uniq -c | sort -rn | head -20

 

如果某个接口调用次数异常高,可能是爬虫高频抓取或被人恶意调用,存在性能和安全隐患。

5.5 来源 IP 分析

统计请求来源 IP 分布,发现异常来源:

 

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 统计来自同一 IP 的请求次数,检测爬虫或攻击
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | awk '$1 > 1000 {print "Suspicious IP: " $2 " Count: " $1}'

 

正常用户行为下,单个 IP 的请求频率不会超过一定阈值。如果某个 IP 每秒发起数百次请求,基本可以判断为爬虫或攻击。

5.6 带宽消耗分析

统计每个请求的流量消耗:

 

# 统计总带宽消耗(单位:字节)
awk -F'"' '{sum+=$10} END {print sum}' /var/log/nginx/access.log

# 统计流量最大的 20 个请求
awk -F'"' '{print $10, $2}' /var/log/nginx/access.log | sort -rn | head -20

 

注意 $body_bytes_sent 是压缩后的大小。如果启用了 gzip,实际传输量会小于原始文件大小。如果带宽消耗异常高,可能是被盗链或者被恶意请求大文件。

5.7 upstream 问题分析

针对代理场景,分析 upstream 健康度:

 

# 提取 upstream 状态码分布
awk -F'"' '{print $14}' /var/log/nginx/access.log | grep -v '-' | sort | uniq -c | sort -rn
# 找出 502/504 错误的请求,分析是哪台 upstream
awk -F'"' '$14 ~ /502|504/ {print $0}' /var/log/nginx/access.log | head -10
# 分析各 upstream 的响应时间差异
awk -F'"' '{print $16, $18}' /var/log/nginx/access.log | grep -v '-' | sort | uniq -c | sort -rn

 

如果 502/504 错误的 upstream_addr 都指向同一台服务器,说明该服务器存在问题。如果 upstream_status 为空但 Nginx 返回了 502,说明 upstream 配置有误或服务未启动。

5.8 错误日志分析

过滤指定级别的错误:

 

# 只看 error 级别以上的日志
grep '[error]' /var/log/nginx/error.log

# 过滤特定错误类型
grep 'connect() failed' /var/log/nginx/error.log

# 统计各类错误出现次数
grep '[error]' /var/log/nginx/error.log | awk -F': ' '{print $NF}' | cut -d'(' -f1 | sort | uniq -c | sort -rn

 

5.9 日志切割配置

Nginx 自身不切割日志,需要通过 logrotate 或内置方式实现:

使用 logrotate:

 

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

 

kill -USR1 信号告诉 Nginx 重新打开日志文件,实现平滑切换,不丢请求。

使用 Nginx 内置日志切割(零停机):

 

mv /var/log/nginx/access.log /var/log/nginx/access.log.$(date +%Y%m%d%H%M%S)
kill -USR1 $(cat /var/run/nginx.pid)

 

这种手动切割方式适合作为备份脚本的一部分,但 logrotate 是更标准的做法。

6. 常见故障排查案例

6.1 案例一:大量 502 Bad Gateway

现象:网站间歇性出现 502 错误,用户反馈时好时坏。

排查步骤:

第一步,查看错误日志:

 

grep '[error]' /var/log/nginx/error.log | grep 'connect() failed' | tail -20

 

第二步,如果 upstream 是 PHP-FPM,检查 PHP-FPM 进程状态:

 

systemctl status php-fpm
ss -lntp | grep php-fpm

 

第三步,检查 PHP-FPM 的慢日志:

 

tail -f /var/log/php-fpm/www-slow.log

 

第四步,检查 PHP-FPM 配置的 max_children 和请求超时时间:

 

# php-fpm.conf
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
request_terminate_timeout = 60s

 

根因:502 通常是 upstream 无响应导致。可能原因包括 PHP-FPM 进程耗尽、PHP 脚本执行超时、数据库连接耗尽、PHP-FPM 进程 OOM 被杀死等。

解决思路:增加 PHP-FPM 的 max_children、延长超时时间、优化 PHP 脚本性能(如增加缓存、减少数据库查询)、增加服务器内存。

6.2 案例二:所有请求响应时间突然变长

现象:服务器 CPU 和内存使用率正常,但所有接口响应时间都增加了 2-3 秒。

排查步骤:

第一步,检查 upstream 响应时间:

 

awk -F'"' '{print $13}' /var/log/nginx/access.log | awk 'NF' | sort | uniq -c | sort -rn | head -10

 

第二步,检查 Nginx 与 upstream 之间的网络延迟:

 

ping -c 10 127.0.0.1
ss -s

 

第三步,检查是否存在大量 keep-alive 连接等待处理:

 

netstat -an | grep ESTABLISHED | wc -l

 

第四步,检查 upstream 的连接池配置:

 

upstream backend {
    server 127.0.0.1:8080;
    keepalive 32;
}

 

根因:响应时间全面增加,通常是 Nginx 与 upstream 之间的连接问题。可能是 keepalive 连接未释放导致连接复用率低、upstream 的 backlog 队列满了导致请求排队、网络层面存在丢包或延迟。

解决思路:确保 upstream 配置了足够的 keepalive 连接数、检查网络设备是否存在问题、调整内核网络参数如 net.core.somaxconn 和 net.ipv4.tcp_max_syn_backlog。

6.3 案例三:来自同一 IP 的异常高频请求

现象:安全监控系统告警发现某个 IP 在短时间内发起了数万次请求。

排查步骤:

第一步,分析该 IP 的请求特征:

 

grep '^异常IP' /var/log/nginx/access.log | awk '{print $2}' | awk -F'"' '{print $2}' | sort | uniq -c | sort -rn | head -20

 

第二步,检查该 IP 访问的路径是否集中在特定接口:

 

grep '^异常IP' /var/log/nginx/access.log | awk -F'"' '{print $2}' | awk '{print $2}' | sort | uniq -c | sort -rn

 

第三步,分析 User-Agent 判断是否为爬虫:

 

grep '^异常IP' /var/log/nginx/access.log | awk -F'"' '{print $8}' | sort | uniq -c | sort -rn

 

根因:可能是恶意爬虫、竞争对手抓取、CC 攻击的前兆。

解决思路:在 Nginx 层封禁该 IP(但需要注意绕过 CDN 时真实 IP 可能不同)、接入 WAF 防护、配置 rate limiting 限流策略、将异常流量打上标记上报安全监控系统。

6.4 案例四:CSS/JS 文件返回 404 但文件实际存在

现象:页面样式错乱,浏览器控制台显示 CSS 文件 404,但登录服务器确认文件存在。

排查步骤:

第一步,确认 404 请求的 URL 和 Referer:

 

grep '404' /var/log/nginx/access.log | grep '.css' | head -10

 

输出会显示请求的完整 URL 和来源页面。

第二步,检查 alias 路径配置是否正确:

 

location /static/css/ {
    alias /data/www/static/css/;
}

 

注意 alias 路径尾部斜杠的含义:带斜杠表示将 URL 中的路径直接拼接到 alias 路径后面;不带斜杠则需要去掉 URI 中对应的前缀。

根因:alias 配置错误是最常见原因。Nginx 的 location 匹配规则也会影响 alias 的解析结果。

解决思路:修正 alias 配置、使用 root 替代 alias(注意两者的路径拼接规则差异)、确认 location 块的匹配优先级。

6.5 案例五:HTTPS 证书配置后部分客户端无法访问

现象:新部署了 SSL 证书,部分旧版本客户端无法建立 HTTPS 连接。

排查步骤:

第一步,查看错误日志中的 SSL 相关错误:

 

grep 'ssl' /var/log/nginx/error.log | tail -50

 

第二步,检查 SSL 协议版本配置:

 

grep -E 'ssl_protocols|ssl_ciphers' /etc/nginx/conf.d/*.conf

 

第三步,使用 openssl 测试证书支持的协议和加密套件:

 

openssl s_client -connect example.com:443 -tls1_1
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

 

第四步,查看日志中记录的 ssl_protocol 和 ssl_cipher:

 

awk -F'"' '{print $25, $26}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

 

根因:配置了过于严格的 SSL 参数,导致旧版本客户端不支持。2026 年 TLSv1.2 的弱加密套件正在被逐步禁用,如果业务需要支持老旧客户端(如旧版 Android APP 或 IE),需要特别配置。

解决思路:根据业务实际情况选择合适的 SSL 配置、优先使用 TLSv1.3、与客户端团队确认支持的最低协议版本。

7. 日志分析与监控联动

7.1 接入 ELK Stack

现代运维体系中,日志集中收集和分析是标准配置。Nginx 日志接入 ELK 的架构如下:

Filebeat 收集日志 -> Logstash 解析过滤 -> Elasticsearch 存储索引 -> Kibana 可视化展示

Filebeat 配置示例:

 

# /etc/filebeat/filebeat.yml
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log
  json.keys_under_root: true
  json.add_error_key: true
  fields:
    log_type: nginx_access
  fields_under_root: true

output.logstash:
  hosts: ["logstash.example.com:5044"]

 

7.2 接入 Prometheus + Grafana

结合 nginx_exporter 可以将 Nginx 的 metrics 暴露给 Prometheus 采集,然后在 Grafana 中可视化展示:

 

# 启动 nginx_exporter
nginx_exporter -nginx.scrape_uri=http://localhost/status

 

Nginx 需要启用 stub_status 模块:

 

server {
    listen 127.0.0.1:8080;
    server_name localhost;
    location /status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}

 

关键监控指标包括:active connections(当前活跃连接数)、accepts(接受的连接总数)、handled(处理的连接总数)、requests(处理的请求总数)、reading/writing/waiting(当前读/写/等待状态的连接数)。

7.3 告警规则设计

基于日志分析的告警策略示例:

5 分钟内 502/504 错误超过 100 次:触发 upstream 告警

5 分钟内来自同一 IP 的请求超过 5000 次:触发 CC 攻击告警

P99 响应时间超过 10 秒:触发性能告警

error.log 中出现 OOM 或 signal 9:触发进程异常告警

8. 生产环境最佳实践

8.1 日志存储策略

单个 Nginx 日志文件不宜过大,建议每日切割后保留 30 天,超过 30 天的日志迁移到对象存储(如 S3、OSS)做冷存储。日志文件权限设置为 640,所有者为 nginx 运行用户,防止敏感信息泄露。

8.2 磁盘空间监控

Nginx 日志目录应该加入磁盘空间监控。经验阈值:磁盘使用率超过 80% 告警,超过 90% 必须立即处理。可以通过 Prometheus 的 node_filesystem_metrics 或 Zabbix 的磁盘监控实现。

 

# 快速查看 Nginx 日志目录大小
du -sh /var/log/nginx/
# 查看日志目录所在磁盘的使用率
df -h /var/log/nginx

 

8.3 敏感信息保护

日志中可能包含敏感信息,需要注意:

禁止将用户密码、Token、身份证号等写入日志

对 query string 中的敏感参数做过滤

定期检查日志内容,发现敏感信息立即处理

日志文件权限控制,防止非授权访问

使用 map 指令过滤敏感参数:

 

map $request_uri $request_filtered {
    ~*(.*)password=(.*) $1password=***$2;
    ~*(.*)token=(.*) $1token=***$2;
    ~*(.*)id_card=(.*) $1id_card=***$2;
    default $request_uri;
}

 

8.4 性能影响评估

日志写入是有性能开销的,特别是高频请求场景。测试数据表明:关闭 access_log 后 QPS 可提升约 5-10%。因此对于超大流量的 Nginx,日志配置需要特别优化:

使用 buffer 和 flush 参数减少磁盘 I/O

使用异步日志写入(需要编译时启用)

对于超大流量场景,考虑采样日志(只记录 1/10 的请求)

静态资源(图片、CSS、JS)单独配置日志关闭,减少无效写入

8.5 规范化日志字段

团队内统一日志格式非常重要。建议在 Confd 或 Consul 中统一管理日志模板,确保所有 Nginx 节点使用相同的日志格式,便于后续日志分析和工具对接。

日志字段命名规范:使用下划线分隔的英文字母,不使用驼峰命名或中文拼音缩写。时间统一使用 ISO 8601 格式。数值类型字段不添加引号,便于后续统计计算。

9. 自检清单

完成日志分析配置后,按以下清单逐项检查:

访问日志格式包含哪些字段:time、remote_addr、request、status、body_bytes_sent、request_time、upstream_*

错误日志级别设置是否正确(生产环境应为 warn 或 error)

日志切割策略是否配置,保留周期是否符合要求

日志目录磁盘空间监控是否配置

敏感信息过滤规则是否配置

日志格式是否支持 ELK/Prometheus 等监控系统接入

日志文件权限是否正确(640,nginx 用户可写)

是否关闭了静态资源的 access_log(可选但推荐)

测试日志写入是否正常:执行 nginx -t 后 reload,验证无报错

健康检查机制是否建立:定期分析日志中的异常指标

10. 总结

Nginx 日志是运维工作的第一手资料。理解每个字段的含义是分析问题的前提,而日志分析方法则是将数据转化为洞察的工具。

访问日志中的 request_time、$upstream_* 是排查性能问题和 upstream 故障的核心字段。错误日志中的 errno 信息是定位 Nginx 自身问题的唯一入口。合理配置日志格式,在保证信息完整性的同时避免过度记录。接入 ELK 或 Prometheus 等监控系统,将被动查询转化为主动告警。

日志分析能力的提升来自实践。遇到问题时,先看日志再查资料,养成这个习惯能让你比大多数运维工程师更快定位根因。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分