nginx模块加载顺序由编译期静态模块顺序和运行时动态模块加载时机共同决定:静态模块顺序由configure参数生成ngx_modules[]数组,内置模块最前、事件模块次之、http模块居中、第三方模块按--add-module从左到右追加;动态模块通过load_module按配置文件书写顺序加载,影响handler/filter链执行顺序。

Nginx 模块加载顺序不能靠配置文件里 load_module 的书写顺序“随意调整”,它本质上由两个独立但相互影响的机制决定:编译期静态模块顺序 和 运行时动态模块加载时机。搞清这两层,才能真正控制模块行为。
静态模块顺序由 configure 参数决定
所有编译进 Nginx 二进制的模块(即非 `.so` 文件),其初始化先后完全取决于 `objs/ngx_modules.c` 中 `ngx_modules[]` 数组的排列。这个数组不是手写的,而是 `./configure` 脚本根据参数自动生成的,规则明确:
- 内置核心模块(如 &ngx_core_module、&ngx_http_core_module)永远排最前
- 事件模块(如 &ngx_epoll_module)紧随其后
- HTTP 框架模块(&ngx_http_module 及其子模块)在中间,其中 &ngx_http_core_module 的
ctx_index固定为 0,是所有 HTTP 模块注册的起点 - 第三方模块按
--add-module=路径从左到右依次追加到数组末尾 - filter 类模块中,&ngx_http_write_filter_module 必须排在最后,否则响应体可能被截断
- 上游健康检查类模块(如 upstream_check)必须比 &ngx_http_proxy_module 初始化更早,否则
check指令无效
动态模块顺序由 load_module 位置控制
动态模块(`.so` 文件)不参与 `ngx_modules[]` 排序,但它们的加载时机和插入点会影响 handler/filter 链的实际执行顺序:
-
load_module指令只能出现在 main 上下文,且必须写在events和http块 之前 - 多个
load_module行按配置文件中的书写顺序依次加载(从上到下) - 虽然不改变初始化次序,但模块注册的 handler 或 filter 会按加载顺序插入到对应阶段链中 —— 例如两个 rewrite 类动态模块,后加载的可能覆盖先加载的钩子
- 若某动态模块依赖另一个模块导出的函数(如
ngx_http_upstream_add()),而被依赖模块是静态编译的,需确保该静态模块已初始化完成(通常没问题);但若两者都是动态模块,则加载顺序必须合理,否则启动时报undefined symbol
验证与调试方法
光写对配置不够,得确认实际效果:
- 查看
objs/ngx_modules.c(仅限静态编译场景),确认第三方模块是否处于预期位置 - 启动时用
nginx -t只能检查语法,真正要看模块是否就位,需观察error.log是否出现module is not binary compatible或符号未定义错误 - 做功能测试:比如启用
geoip2后访问含$geoip2_city_name的 location,看变量是否输出;或开启健康检查后故意停掉后端,观察状态是否变为down
常见错误规避建议
很多问题其实源于顺序误判:
- 不要把
load_module写在http块内部,Nginx 会直接报错退出 - 避免同时静态编译两个功能重叠的 rewrite 模块(如两个增强版
ngx_http_rewrite_module),后初始化的会覆盖前者注册的正则处理器 - 使用
proxy_pass+ 健康检查时,确保 upstream_check 是静态编译且排在 proxy_module 之前,或作为动态模块且在 proxy 相关配置生效前加载 - gzip 和 brotli 共存时,注意二者同属 output filter,brotli_filter 应排在 gzip_filter 之后(通过动态模块加载顺序或静态编译顺序控制),否则压缩可能被跳过











