摘要:独立站或反代站点短时间内出现大量 4xx、5xx 时,先分清该看访问日志里的 $status,还是错误日志里的原因。依据 Nginx 官方文档,核对 error_log 级别、access_log 默认 combined 格式、缓冲写入、条件记录,以及内部跳转后日志记在哪个 location。查询日期 2026-11-14。只放官网普通链接(affiliate_later)。

依据:Nginx 文档 error_log、ngx_http_log_module(含 access_log、log_format)、A debugging log。中文文档入口见 error_log(中文) 与 ngx_http_log_module(中文),若中英文表述有出入,以你打开时的英文原版为准。查询日期:2026-11-14(北京时间)。只放官网普通链接(affiliate_later: 1)。指令默认值、日志路径和级别以你服务器上的配置为准,可用 nginx -T 核对,不编发行版特有的默认路径,也不构成运维承诺。

站点短时间内出现一批 404、403,或突然冒出 502、504 时,先不要只盯面板上的状态码曲线。Nginx 把两类信息写在不同地方:访问日志(access_log)记录这次请求最终给出的 $status、请求行和耗时;错误日志(error_log)记录处理过程中达到设定级别的内部消息,例如连不上上游。502、504 怎么从反代超时和 WebSocket 升级上排查,见 Nginx 反向代理排错:502/504、上游超时与 WebSocket;站点根本打不开、证书或 DNS 先坏了,见 网站 404 / 502 排查清单。本文不重写那些配置项,只解决一件事:日志该从哪份文件读、字段怎么对,以及为什么有时访问日志有 5xx、错误日志却是空的。

1. 先找到两份日志的实际路径和级别

官方文档里,error_log 的默认写法是 error_log logs/error.log error;,access_log 的默认写法是 access_log logs/access.log combined;。这里的 logs/... 是相对于 Nginx 安装前缀的路径,不是所有 Linux 发行包都会把文件放在同一处。不要把 /var/log/nginx/access.log 当成文档默认值。要看本机到底写到哪里,在服务器上执行:

nginx -T 2>/dev/null | grep -E 'error_log|access_log|log_format'

把输出里的路径、格式名和级别记下来,后面的检索都对着这些文件做。同一配置层级可以写多条 error_log 或 access_log(error_log 自 1.5.2 起支持同级多条)。如果某一层写了 access_log off;,文档说明它会取消当前层上的所有 access_log 指令,这一层就不会再写访问日志。

error_log 还可写到 stderr、以 syslog: 为前缀的 syslog,或以 memory: 为前缀的循环内存缓冲区(文档写明内存方式一般用于调试,自 1.7.11 起)。访问日志也可以用 syslog: 前缀。如果你在配置里找得到路径、在磁盘上却找不到对应文件,先核对是不是写到了 syslog 或内存,而不是先怀疑日志被删了。

2. 错误日志:级别决定哪些原因会被记下

error_log 的句法是 error_log file [level] [json];,可用上下文包括 main、http、mail、stream、server、location。第二个参数是日志级别,从低到高为:debug、info、notice、warn、error、crit、alert、emerg。文档写明:设定某一级别后,会记录该级别以及更严重的全部消息。省略级别时按 error 处理。因此默认的 error 会记下 error、crit、alert、emerg,但不会记下 warn、notice、info 或 debug。

这一点直接关系到「5xx 已经出现,错误日志却几乎是空的」。若原因落在 warn 或更低级别,而当前 error_log 停在默认的 error,访问日志里能看到 $status 是 502,错误日志却可能没有对应行。临时把某一 server 或全局的级别调到 warn 或 notice 再观察,是在文档允许的范围内;改完配置后要按你平时的方式重载 Nginx,并在问题看清后改回原级别,避免磁盘被低级别消息打满。debug 还要求编译时带 --with-debug,细节见 A debugging log,不要在未开启调试编译的二进制上期待 debug 行出现。

![Nginx 文档 error_log:句法为 error_log file [level] [json];默认 error_log logs/error.log error;级别从 debug 到 emerg,设定某一级别会记录该级别及更严重的消息;默认 error 会记下 error、crit、alert、emerg。页面下方的 json 参数注明属于商业订阅(2026-11-14 截自 nginx.org 官方文档)](img-nginx-error-log-levels-20261114.png)

文档还提到:json 参数(1.29.8)可按 JSON 写错误日志,并注明该参数属于商业订阅;JSON 条目不能超过 2 KB,超出部分会截断。开源安装里更常见的仍是普通文本错误日志,不要按 JSON 字段名去套你现有的 error.log。

读错误日志时,先对准时间窗口,再看消息本身是否指向上游连接、权限或配置。官方 JSON 示例里出现过 connect() failed、Connection refused 这类原文,文本日志里也可能看到类似表述,但具体一行以你文件里的原文为准,不要把示例里的字段名当成所有版本都一定存在的列。

3. 访问日志:先认 combined 格式里的状态码和耗时

ngx_http_log_module 写明:请求是在处理结束时所在的 location 上下文中记录的;若处理过程中发生了内部跳转,这个 location 可能和最初匹配的那个不同。伪静态、重写或内部重定向之后,日志里的 $request、$status 要对着最终处理位置来读。WordPress 伪静态和 404 循环怎么核对配置,见 伪静态与固定链接:404 循环与 Nginx rewrite,本篇不重写 rewrite 规则。

未指定格式时,使用预定义的 combined 格式。官方给出的定义是:

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

![Nginx 文档预定义 combined 格式:字段依次为 $remote_addr、$remote_user、[$time_local]、"$request"、$status、$body_bytes_sent、"$http_referer"、"$http_user_agent"(2026-11-14 截自 nginx.org 官方文档)](img-nginx-log-format-combined-20261114.png)

字段顺序可以记成:客户端地址、用户(取不到时记为 -)、本地时间、请求行、响应状态 $status、发往客户端的主体字节数、Referer、User-Agent。在默认 combined 下,状态码紧跟在引号包裹的请求行后面,例如 "GET /path HTTP/1.1" 502。检索 4xx、5xx 时,要对准这个位置,避免把请求路径或其它数字误当成状态码。

同一模块还列出了只在写日志时存在的变量(现代版本里其中不少也已作为普通变量可用),和集中排障直接相关的包括:

  • $status:响应状态
  • $request_time:请求处理时间,秒,毫秒精度;从刚开始向客户端读取字节,到向客户端发完最后字节并写日志,之间经过的时间
  • $body_bytes_sent:combined 里使用的字段,表示已发送的响应主体字节数
  • $bytes_sent:发送给客户端的字节数
  • $time_local / $time_iso8601:本地时间(后者为 ISO 8601)
  • $request_length:请求长度(含请求行、头部和请求体)

文档写明:在默认转义下,若变量值不存在,日志里会记一个连字符 -。看到 - 时,先确认是「这个字段当时没有值」,不要把它读成状态码。

如果你的 access_log 行带了自定义 log_format 名称,先在配置里把该格式的字段顺序对出来,再检索。不要用 combined 的位置去套自定义格式。

上传过大导致的 413,还要同时看 client_max_body_size 等限制,见 WordPress 上传文件过大时的 PHP 与 Nginx 限制,那一篇写限制本身,本篇只提醒:这类请求在访问日志里通常以 4xx 状态出现,原因不一定会写进默认级别的错误日志。

4. 访问日志有状态、错误日志没有原因:先核缓冲和条件记录

两份日志对不上时,除了上一节的级别问题,文档还写了另外两个常见原因。

写入可能被缓冲。 access_log 若带了 buffer 或 gzip 参数,写入会先进入缓冲区。文档写明,数据会在这几种情况下落到文件:下一条日志写不进缓冲区;缓冲数据已超过 flush 所设时间(1.3.10 / 1.2.7);工作进程正在重新打开日志文件或正在关闭。刚出现一批 5xx、立刻去 tail 文件却还是空的,有可能是还压在缓冲区里。可以等一个 flush 周期,或按你现有的运维方式重新打开日志(常见做法是重载或对日志做一次 reopen)后再看。不要在这一步修改业务配置。带 gzip 时,文档写明日志按原子块压缩,可随时用 zcat 读取。

Nginx 访问日志与错误日志:4xx 和 5xx 集中出现时怎么核对(2026)-有序
Nginx 文档 access_log:未指定格式时使用 combined;buffer 或 gzip 时会缓冲写入,并在下一条写不进缓冲区、超过 flush 时间、或工作进程重新打开日志/关闭时落到文件(2026-11-14 截自 nginx.org 官方文档)

条件记录可能把部分状态码排除了。 access_log 的 if 参数(1.7.0)用于条件记录:条件计算结果为 0 或空字符串时,该请求不会记入这条访问日志。官方示例用 map $status $loggable,把 2xx 和 3xx 映射为 0,其它为 1,于是 2xx、3xx 不写日志,4xx、5xx 仍会写。若你的配置把条件写反了,或把 4xx、5xx 也映射成了 0,访问日志里就会看不到这批错误状态。核对时把该 server / location 上的 access_log ... if= 和对应的 map 一起看完。

Nginx 访问日志与错误日志:4xx 和 5xx 集中出现时怎么核对(2026)-有序
Nginx 文档 access_log 的 if 参数:条件为 0 或空字符串时不记录该请求;示例用 map $status $loggable 排除 2xx 和 3xx,4xx、5xx 仍会写入(2026-11-14 截自 nginx.org 官方文档)

反代场景下,访问日志里的 502、504 往往要回到错误日志里找上游失败原因,再对照 502/504 与上游超时 里的超时项。多站点证书和强制 HTTPS 导致的 502,见 反向代理多站点:证书、强制 HTTPS 与常见 502。若页面能打开但内容是旧的,那是缓存层的问题,见 Cloudflare 缓存与源站:更新后前台仍是旧页怎么核对,不要和 5xx 日志混在一条排查线里。

5. 可勾核对清单

  1. □ 已用 nginx -T(或等价方式)确认本机 error_log、access_log 的实际路径、格式名和级别,没有把文档里的 logs/error.log 或某个发行包路径直接当成当前机器的文件
  2. □ 已分清:访问日志看 $status 和 $request;错误日志看达到设定级别的内部消息;内部跳转后,访问日志记在处理结束时所在的 location
  3. □ 默认 combined 下,已按「请求行后面的数字」读取状态码;自定义格式则先对照 log_format 字段顺序
  4. □ 若访问日志已有 4xx/5xx、错误日志没有对应原因,已检查当前级别是否为默认的 error(因而不会记录 warn 及更低),以及是否启用了 buffer / gzip / flush,或 if= 条件把这些状态排除了
  5. □ 需要更细的错误原因时,只在理解 调试日志 的编译前提(--with-debug)后再考虑 debug,并在看完后改回原级别
  6. □ 502/504 的超时和上游参数,转到已有的反代排错文,不在本篇改这些值

6. 风险提示

  • 指令默认值、路径和级别以你打开时的官方文档以及服务器上的实际配置为准;发行包、容器镜像可能改过路径。
  • 把错误日志调到 debug 或长时间停在 info / notice,可能迅速占满磁盘,也会在日志里留下更多请求细节。看完应改回原级别。
  • 日志里可能含 IP、请求路径、Cookie 或鉴权相关头,转发或贴到外部工具前先做脱敏。
  • 本文不构成对具体故障原因或修复时间的承诺。

7. 相关阅读

来源: Nginx 官方文档 error_log、ngx_http_log_module、A debugging log;中文页面 error_log、ngx_http_log_module。查询日期 2026-11-14(北京时间)。