问题现象
在 OpenResty 的同一个 location 中,同时使用 access_by_lua_block 和 return 时,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),按顺序执行:
POST_READ
SERVER_REWRITE
FIND_CONFIG
REWRITE
POST_REWRITE
PREACCESS
ACCESS
POST_ACCESS
PRECONTENT
CONTENT
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 块中完成所有响应逻辑。
最佳实践
避免在同一个 location 中混合使用
return和access_by_lua_block,因为阶段顺序决定了return会先执行。如需在 access 阶段进行校验,并根据结果返回不同内容,请使用
content_by_lua_block。开启调试日志(
error_log logs/error.log debug;)可以清晰看到每个阶段的执行情况,便于排查阶段顺序问题。
通过理解 Nginx 的阶段模型,可以更好地掌握 OpenResty 中各种指令的执行顺序,避免此类混淆。