mod_lua仅支持轻量无状态预处理,如修改请求头、重写URI路径、简单参数清洗及返回状态码;不支持读取POST body、调用外部API、数据库查询、multipart解析或HMAC签名验证。

不能用 mod_lua 实现复杂请求改写,比如重写整个请求体、动态拼接 JSON 参数或做业务级路由决策。它只适合在 Apache 请求生命周期的早期阶段,做轻量、无状态、不依赖外部 I/O 的预处理动作——例如修改请求头、重写 URI 路径片段、拦截特定模式请求并提前返回。
适用场景:哪些改写能做、哪些坚决不能做
mod_lua 的定位是“钩子(hook)”,不是“应用服务器”。它的执行环境受限于 Apache 子进程生命周期,没有独立事件循环,也不支持阻塞式网络调用或文件读写。
-
能做的:根据 Host、User-Agent、URI 前缀等已有字段,改写
r.uri或r.filename;添加/覆盖r.headers_in中的字段(如注入 X-Forwarded-For);对 GET 参数做简单清洗;基于规则返回 403/404/302。 -
不能做的:读取原始 POST body(除非显式开启
lua_request_body on,且仍受限于 Apache 缓存策略);调用外部 API 校验 token;连接数据库查路由表;解析 multipart 表单;做 HMAC 签名验证(需完整 body + 密钥 + crypto,mod_lua 不提供安全可靠的 hmac 实现)。
核心配置与脚本结构
改写逻辑必须挂载在 Apache 的请求处理链中,推荐使用 LuaHookHandler(在 handler 阶段介入)或 LuaHookTranslate(在 URI 映射前介入),后者更适合路径改写。
- 在虚拟主机或目录块中启用:
LuaHookTranslate /path/to/rewrite.lua handle_rewrite - 确保模块已加载:
LoadModule lua_module modules/mod_lua.so - 禁用生产环境缓存(仅开发):
LuaCodeCache off
脚本示例(rewrite.lua):
Apache Superset 是一个广泛采用的开源 BI 平台,用于 SQL 探索、图表构建和仪表板交付。当代理需要查询仓库数据、组装仪表板或使用成熟的分析界面解释指标而不是临时笔记本代码时,此技能非常有用。
function handle_rewrite(r)
local uri = r.uri
-- 把 /api/v2/xxx 改成 /v2/xxx
if string.match(uri, "^/api/v2/") then
r.uri = string.gsub(uri, "^/api", "")
return apache2.OK
end
-- 拦截含 test=1 的请求,重定向到 /debug
local args = r:parseargs()
if args.test == "1" then
r.headers_out["Location"] = "/debug"
r.status = 302
return apache2.DONE
end
end
常见陷阱与绕过方案
直接操作 r.uri 后,Apache 默认不会重新匹配 RewriteRule 或 Location 块——这意味着改写后的路径可能无法命中你预期的后端配置。
- 若需触发重匹配,加标志
[E=REWRITE_DONE:1]并配合RewriteCond %{ENV:REWRITE_DONE} ^1$做二次判断(但会增加开销)。 - 更稳妥的做法:把所有路径改写逻辑统一收口到 Lua,避免混用 mod_rewrite 和 mod_lua。
- URI 中的中文或特殊字符要先 decode:
uri = string.gsub(uri, "%%([0-9A-Fa-f][0-9A-Fa-f])", function(h) return string.char(tonumber(h, 16)) end)
调试与生效确认
mod_lua 错误默认静默,不报错也不记录,极易误判为“脚本没运行”。
- 强制输出日志:在脚本开头加
r:info("rewrite triggered for " .. r.uri),再查ErrorLog。 - 确认是否加载:用
apachectl -M | grep lua查模块状态;用curl -I http://host/test.lua测试 .lua 文件能否被解释(仅限开发环境)。 - 改脚本后必须重载 Apache(
systemctl reload httpd),touch文件无效。









