Administrator
发布于 2026-03-25 / 32 阅读
0

OpenResty 中 return 与 access_by_lua_block 的执行顺序问题

问题现象

在 OpenResty 的同一个 location 中,同时使用 access_by_lua_blockreturn 时,access_by_lua_block 中的代码并未执行,而 return 直接生效。例如:

location /lua-test {
    access_by_lua_block {
        ngx.log(ngx.ERR, "LUA_TEST: access_by_lua_block executed")
        ngx.exit(403)
    }
    return 200 "should not reach";
}

期望:先执行 access 块,返回 403。
实际:直接返回 200,且 error.log 中没有 access 块的日志。

原因分析

Nginx 请求处理阶段顺序

Nginx 将请求处理分为多个阶段(phase),按顺序执行:

  1. POST_READ

  2. SERVER_REWRITE

  3. FIND_CONFIG

  4. REWRITE

  5. POST_REWRITE

  6. PREACCESS

  7. ACCESS

  8. POST_ACCESS

  9. PRECONTENT

  10. CONTENT

  11. LOG

  • return 指令属于 ngx_http_rewrite_module 模块,它在 REWRITE 阶段 执行,并会立即终止请求(设置响应状态码和内容,跳过所有后续阶段)。

  • access_by_lua_block 属于 Lua 模块,它在 ACCESS 阶段 执行。

由于 REWRITE 阶段在 ACCESS 阶段之前,当 return 存在时,它会在 REWRITE 阶段就返回响应,导致后续的 ACCESS 阶段(即 access_by_lua_block)和 CONTENT 阶段都不会执行。

因此,return 总是先于 access_by_lua_block 执行,无法实现“先执行 access 逻辑,再执行 return”的效果。

正确实现方式

方案1:使用 content_by_lua_block 替代 return

如果需要在 access 校验通过后返回内容,应将 return 替换为 content_by_lua_block,并在 access 块中决定是否继续。

location /lua-test {
    access_by_lua_block {
        -- 权限校验逻辑
        local auth = ngx.var.http_authorization
        if not auth then
            ngx.exit(403)   -- 校验失败,终止请求
        end
        -- 校验通过,继续执行 content 阶段
    }
    content_by_lua_block {
        ngx.say("Access granted")
    }
}

这样,access 块先执行,如果校验失败则返回 403;校验通过则继续执行 content 块返回内容。

方案2:在 access 块中直接返回响应

如果校验失败后需要返回特定内容,也可以在 access 块中直接使用 ngx.say() + ngx.exit() 返回响应,无需 content 块。

location /lua-test {
    access_by_lua_block {
        local auth = ngx.var.http_authorization
        if not auth then
            ngx.say("Unauthorized")
            ngx.exit(401)
        end
        -- 校验通过,继续(可执行后续 content 块或其他操作)
    }
    content_by_lua_block {
        ngx.say("Welcome")
    }
}

为什么使用 content_by_lua_block 时 access 块能执行?

access_by_lua_block + content_by_lua_block 的组合中,没有 return 指令,因此 REWRITE 阶段不会提前终止请求,请求会依次经过 ACCESS 阶段(执行 access 块)和 CONTENT 阶段(执行 content 块),所以 access 块能正常执行。

总结

  • return 指令在 REWRITE 阶段执行,会跳过所有后续阶段(包括 ACCESS 和 CONTENT)。

  • access_by_lua_block 在 ACCESS 阶段执行,无法与 return 共存以实现“先 access 后 return”。

  • 正确做法:用 content_by_lua_block 替代 return,或在 access 块中完成所有响应逻辑。

最佳实践

  1. 避免在同一个 location 中混合使用 returnaccess_by_lua_block,因为阶段顺序决定了 return 会先执行。

  2. 如需在 access 阶段进行校验,并根据结果返回不同内容,请使用 content_by_lua_block

  3. 开启调试日志error_log logs/error.log debug;)可以清晰看到每个阶段的执行情况,便于排查阶段顺序问题。

通过理解 Nginx 的阶段模型,可以更好地掌握 OpenResty 中各种指令的执行顺序,避免此类混淆。