Why Does Nginx Only Return the Last Set Variable Value?

The article explains that Nginx processes requests in multiple phases, not in the order written in the configuration, showing how rewrite and content phases affect variable assignments and why only the last set value is output.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Why Does Nginx Only Return the Last Set Variable Value?

We define a location block named phase_echo that sets a variable $name multiple times and echoes it. When the request curl -i http://openresty.tinywan.com/phase_echo is made, the response contains only the last value "Tinywan 开源技术小栈".

Why does the output always show the last assigned value?

The reason is that Nginx handles each request through a series of processing phases, not strictly following the order of directives in the configuration file. The example involves two phases: rewrite and content. set belongs to the rewrite phase. echo belongs to the content phase.

During execution, directives in the rewrite phase run before those in the content phase. Therefore, all set commands execute first, and the subsequent echo commands output the variable's final value.

set $name "Tinywan";
set $name "开源技术小栈";
set $name "Tinywan 开源技术小栈";
echo $name;
echo $name;
echo $name;

This explains why the response shows only "Tinywan 开源技术小栈".

Nginx Execution Phases

Nginx processes a request through 11 phases in the following order: post-read, server-rewrite, find-config, rewrite, post-rewrite, preaccess, access, post-access, try-files, content, and log.

Each phase runs its directives before moving to the next phase.

Execution Phase Example
Nginx phase diagram
Nginx phase diagram

post-read : reads request headers; modules like ngx_realip can modify the perceived client IP.

server-rewrite : rewrites the request URI in the server block using ngx_rewrite directives.

find-config : core phase that matches the request to a location block.

rewrite : runs location rewrite directives and Lua set_by_lua / rewrite_by_lua commands.

post-rewrite : handles internal redirects after rewrite.

preaccess : prepares access control; modules like ngx_limit_req and ngx_limit_zone run here.

access : performs access checks; standard ngx_access and Lua access_by_lua run here.

post-access : finalizes access decisions using directives such as satisfy.

try-files : processes the try_files directive, performing internal redirects if needed.

content : generates the HTTP response body; the most critical phase for output.

log : writes access logs.

OpenResty Execution Mechanism

OpenResty flow
OpenResty flow

OpenResty adds Lua phases to the Nginx pipeline, resulting in four major stages (initialization, rewrite/access, content, log) and eleven sub‑phases.

Initialization: init_by_lua_file, init_worker_by_lua_file, ssl_certificate_by_lua_file.

Rewrite/Access: set_by_lua_file, rewrite_by_lua_file, access_by_lua_file.

Content: content_by_lua_file, balancer_by_lua_file.

Log: log_by_lua_file, plus header/body filter phases.

Lua directives come in three forms: xxx_by_lua (inline string), xxx_by_lua_block (code block), and xxx_by_lua_file (external file). Using xxx_by_lua_file separates configuration from business logic.

OpenResty vs Nginx Phase Mapping

Phase mapping
Phase mapping

Example configuration demonstrates all Lua phases within a location /run_phase block, each logging a message to the error log. The log output confirms that Nginx executes phases sequentially.

server {
    listen 80;
    server_name openresty.tinywan.com;

    location /run_phase {
        set_by_lua_block $a { ngx.log(ngx.ERR, "Tinywan is set_by_lua_block phase") }
        rewrite_by_lua_block { ngx.log(ngx.ERR, "Tinywan is rewrite_by_lua_block phase") }
        access_by_lua_block { ngx.log(ngx.ERR, "Tinywan is access_by_lua_block phase") }
        content_by_lua_block { ngx.log(ngx.ERR, "Tinywan is content_by_lua_block phase") }
        header_filter_by_lua_block { ngx.log(ngx.ERR, "Tinywan is header_filter_by_lua_block phase") }
        body_filter_by_lua_block { ngx.log(ngx.ERR, "Tinywan is body_filter_by_lua_block phase") }
        log_by_lua_block { ngx.log(ngx.ERR, "Tinywan is log_by_lua_block phase while logging request") }
    }
}

Running curl -i http://openresty.tinywan.com/run_phase and checking the error log shows messages from each phase in the expected order, confirming the phase‑based execution model.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ConfigurationRequest Processingrewritecontentexecution phases
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.