Nginx 的 error_log 不记录动态模块加载失败的早期错误,因模块加载早于日志系统初始化;应使用 nginx -e stderr 强制终端输出启动期错误,再结合 nginx -t 定位报错指令、检查 load_module 配置及 .so 文件存在性与依赖。

Nginx 的 error_log 本身不会记录动态模块加载失败的早期错误,因为模块加载发生在日志系统初始化之前。所以单靠翻看 error.log 常常一无所获——文件可能是空的,或只写入了后续阶段的报错。真正有效的排查,得绕过日志文件,直击启动过程。
直接输出启动期错误到终端
模块未加载、指令不识别、依赖库找不到等问题,大多在 nginx 进程刚启动、还没来得及打开 error.log 文件时就已发生。此时必须强制把错误打到屏幕:
nginx -c /etc/nginx/nginx.conf -e stderr
-
-e stderr是关键:它让所有初始化阶段的错误(包括unknown directive "lua_code_cache"、failed to load module: ngx_http_geoip2_module.so、dlopen() failed for ...)直接输出到当前终端 - 若用 systemd 管理,临时别走服务命令,就手动执行这行;看到报错后立刻停手,不用等它“启动成功”
常见输出示例:
-
nginx: [emerg] unknown directive "brotli" in /etc/nginx/conf.d/app.conf:15→ 对应模块没加载或未编译 -
nginx: [emerg] dlopen() "/usr/local/nginx/modules/ngx_http_brotli_filter_module.so" failed (/lib64/libbrotlidec.so.1: cannot open shared object file)→ 动态库路径缺失
结合 nginx -t 定位配置上下文
nginx -t 虽不打印底层原因,但能精准指出哪一行、哪个指令触发了问题:
- 运行
nginx -t,记下报错中的文件名和行号(如/etc/nginx/conf.d/api.conf:22) - 打开该行,看用了什么指令(如
geoip2_country_code、stream、lua_shared_dict) - 根据指令名反查所属模块:
-
geoip2_*→ngx_http_geoip2_module -
stream { ... }→--with-stream(静态)或ngx_stream_core_module.so(动态) -
lua_*→ngx_http_lua_module.so
-
- 再去检查这个模块是否已通过
load_module加载,.so文件是否存在且可读
查 error_log 只作为辅助验证
当 nginx -e stderr 已确认是模块问题后,再查 error.log 可用于交叉验证运行时行为:
tail -n 30 /var/log/nginx/error.log | grep -i -E "(module|failed|dlopen|load)"
重点关注这些关键词:
-
failed to load module→load_module路径错、文件不存在、权限不足(ls -l $(nginx -p '' -V 2>&1 | grep prefix | awk '{print $2}')/modules/) -
dlopen.*: cannot open shared object file→ 缺少.so依赖(如libbrotlidec.so.1),需用ldd和ldconfig排查 -
module not found或invalid number of arguments→ 模块已加载但版本不兼容(如 Nginx 1.25+ 与旧版 Lua 模块)
确保日志路径本身可写(防静默退出)
如果 error_log 所在目录不可写(比如 /var/log/nginx 不存在或属主不是 nginx 用户),Nginx 可能在连错误都来不及输出前就退出。务必提前检查:
# 查配置中 error_log 指向哪 nginx -T 2>/dev/null | grep "error_log" # 创建目录并赋权(以 nginx 用户为例) mkdir -p /var/log/nginx chown nginx:nginx /var/log/nginx chmod 755 /var/log/nginx
不复杂但容易忽略


















