Nginx Rewrite指令采用“编译为虚拟机指令+运行时解释执行”两阶段设计:配置阶段调用ngx_http_rewrite_rule_compile()和ngx_http_script_compile()将正则与变量表达式编译为函数指针链,请求阶段由ngx_http_script_run()逐条调用code函数完成字符串拼接。

阅读 Nginx Rewrite 模块中脚本指令(如 rewrite、return、set 等)的编译与执行逻辑,核心在于理解其“编译为虚拟机指令 + 运行时解释执行”的两阶段设计。它不依赖外部解释器,而是用一套轻量级自定义字节码(`ngx_http_script_t`)实现高效、安全的字符串处理。
从配置解析开始:rewrite 指令如何触发编译
Nginx 在配置加载阶段(`ngx_conf_handler`)遇到 rewrite 指令时,会调用 ngx_http_rewrite_rule_compile()(位于 src/http/modules/ngx_http_rewrite_module.c)。该函数负责:
- 将配置行中的正则表达式(如
^/old/(.*)$)编译为 PCRE 字节码,并缓存到ngx_http_rewrite_loc_conf_t->rules - 将重写目标(如
/new/$1)和标志(last、break等)封装为ngx_http_rewrite_rule_t - 关键一步:调用
ngx_http_script_compile()(在src/http/ngx_http_script.c),把目标 URI 字符串(含变量如$1、$args)编译成可执行的脚本指令数组(ngx_http_script_code_pt函数指针链)
脚本编译本质:把变量引用转为“操作码+偏移”序列
ngx_http_script_compile() 不生成机器码,而是构建一个线性指令数组,每个元素是:
- 一个函数指针(如
ngx_http_script_copy_code、ngx_http_script_var_code、ngx_http_script_value_code) - 紧跟其后的运行时所需数据(如变量索引
var->index、字符串长度、字面值地址)
例如 set $a "$host:$server_port"; 编译后,脚本数组类似:[copy_code, 'host:', 5] → [var_code, host_var_index] → [copy_code, ':', 1] → [var_code, port_var_index]
执行时按序调用这些 code 函数,拼接结果到目标 buffer。
运行时执行:在请求处理阶段解释脚本
当请求进入 rewrite 阶段(NGX_HTTP_REWRITE_PHASE),Nginx 调用 ngx_http_script_run()(同一文件),传入:
- 已编译的 script 数组(
ngx_http_script_engine_t *e) - 当前请求上下文(
r),用于获取变量值 - 输出 buffer(如重写后的 URI)
引擎逐条执行 code 函数:
– ngx_http_script_var_code 查 r->variables[var_index] 获取变量值并拷贝;
– ngx_http_script_copy_code 直接 memcpy 字面字符串;
– ngx_http_script_value_code 处理整数字面量;
所有操作都在内存 buffer 中完成,无系统调用、无动态内存分配(除初始 buffer 扩容),保证高性能。
调试与验证的关键位置
想确认某条 rewrite 是否被正确编译或执行,可重点关注:
-
ngx_http_rewrite_handler():主入口,遍历 location 的 rewrite rules -
ngx_http_script_run():单步执行脚本的核心循环 -
ngx_http_get_variable():实际取变量值的地方(变量可能来自请求头、内置变量或 set 指令) - 加日志:在
ngx_http_script_var_code内打印e->ip(当前指令指针)和value,观察变量展开过程
注意:rewrite 执行有严格顺序和阶段限制(如 if 块内只允许部分指令),很多行为异常源于阶段错位或变量未就绪,而非脚本本身错误。










