核心是用Lua在header_filter_by_lua_block阶段删、改、控响应头:移除Set-Cookie和X-Debug-*类头,替换Server为"nginx",掩码X-User-ID,HTTPS下需确保SSL终止在Nginx。

直接在 Nginx 层面对 HTTPS 响应头做隐私脱敏,核心是用 Lua 动态干预响应阶段,不依赖后端改造,也不暴露原始敏感字段。关键不在“加功能”,而在“删、改、控”——删掉不该传的头,改掉可脱敏的值,控制哪些头能被下游看到。
哪些响应头需要重点脱敏
常见需处理的敏感响应头包括:
- Set-Cookie:含 session_id、token、用户标识等,必须移除或重写 domain/path/secure/http-only 属性
- X-User-ID、X-Auth-User、X-Real-IP(非可信来源):后端注入的业务标识,若未校验或未加密,需过滤或掩码
- Server、X-Powered-By:泄露技术栈,建议统一抹为 generic 值(如 Server: nginx)
- X-Trace-ID、X-Request-ID(若含用户上下文):仅保留全局唯一性,剥离可推导身份的部分
用 Lua 在响应头阶段精准干预
Nginx 的 header_filter_by_lua* 是唯一能在响应头已生成、但尚未发给客户端时介入的阶段。它运行在 filter 链末端,不影响 body,性能开销极低。
典型配置示例:
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
<pre class="brush:php;toolbar:false;">header_filter_by_lua_block {
-- 移除 Set-Cookie(全站禁用 cookie 下发)
ngx.header["Set-Cookie"] = nil
-- 替换 Server 头
ngx.header.Server = "nginx"
-- 对 X-User-ID 做固定长度掩码(如 12345678 → 12****78)
local uid = ngx.header["X-User-ID"]
if uid and type(uid) == "string" and #uid >= 8 then
ngx.header["X-User-ID"] = string.sub(uid, 1, 2) .. "****" .. string.sub(uid, -2)
end
-- 清除所有以 X-Debug- 开头的调试头
for k, v in pairs(ngx.header) do
if type(k) == "string" and string.match(k, "^X%-Debug%-") then
ngx.header[k] = nil
end
end
}}
HTTPS 场景下的特殊注意事项
HTTPS 不改变 Lua 对响应头的操作逻辑,但需注意三点:
- SSL 终止点必须在 Nginx(即 Nginx 解密 HTTPS),否则 Lua 无法读取或修改明文响应头
- 若使用 CDN 或 TLS 卸载设备在前,确保其透传关键头(如
X-Forwarded-Proto),且不擅自添加敏感头 - 避免在
body_filter_by_lua中操作响应体来“补救”头信息——头已发出再改无效,且易破坏 HTTP/2 流帧结构
更安全的替代方案:由后端注入可信脱敏头
比起 Nginx 动态加工,更推荐让后端统一输出已脱敏的响应头:
- 后端生成
X-Anonymized-ID替代X-User-ID,值为不可逆哈希或 token 化结果 - 后端设置
Set-Cookie时显式指定Path=/; Secure; HttpOnly; SameSite=Lax,Nginx 不做任何修改,只确保不新增其他 cookie - Nginx 仅做“白名单放行”:只保留明确允许的头,其余一律清除,例如:
header_filter_by_lua_block {
local safe_headers = {
"Content-Type", "Content-Length", "X-Request-ID",
"X-RateLimit-Limit", "X-RateLimit-Remaining"
}
for k, _ in pairs(ngx.header) do
if not table.contains(safe_headers, k) then
ngx.header[k] = nil
end
end
}


















