根据这个问题的标题,我如何记录客户端浏览器在Nginx中发送的所有标头?我还想记录响应头。请注意,我使用nginx作为反向代理。
完成文档后,我了解到我可以记录一个特定的标题,但我想记录所有的标题。
我会接受hacky解决方案!
答案 0 :(得分:14)
经过大量研究,我可以得出结论,开箱即用是不可能的。
更新 - 您可以使用Lua附带的openresty。使用Lua可以做很酷的事情,包括记录所有标题,Redis或其他服务器
答案 1 :(得分:11)
正如@gauravphoenix所说,你需要Lua附带的opnresty。请参阅https://github.com/openresty/lua-nginx-module/进行安装。一旦它运行,然后添加nginx
header_filter_by_lua_block {
local h = ngx.req.get_headers()
for k, v in pairs(h) do
ngx.log(ngx.ERR, "Got header "..k..": "..v..";")
end
}
检查错误日志。
答案 2 :(得分:2)
还有两个选项可以在 nginx 中记录标头。
njs 可以是来自包存储库的 installed 并且预安装在官方 nginx docker 镜像中。 njs 至少从 1.9.15 版本的 nginx(相当旧)开始可用,但最好使用更新的版本。
安装后在nginx配置中启用njs http模块:
load_module modules/ngx_http_js_module.so;
标题可能会记录到错误或访问日志中。
HTTP Request 对象具有键值格式的 headersIn, headersOut 字段,重复的标题合并在该字段中,rawHeadersIn, rawHeadersOut 是数组原始标头数组。
创建js模块,使用json序列化headers:
# /etc/nginx/headers.js
function headers_json(r) {
return JSON.stringify(r.headersIn)
}
export default {headers_json};
导入js模块,声明变量并添加到log_format中:
http {
js_import headers.js;
js_set $headers_json headers.headers_json;
# Using custom log format here
log_format main '$remote_addr'
'\t$remote_user'
'\t$time_local'
'\t$request'
'\t$status'
'\t$headers_json';
默认情况下访问日志中的字符串被转义,所以你会得到这样的东西:
# curl -H 'H: First' -H 'H: Second' localhost:8899
172.17.0.1 - 16/Apr/2021:08:46:43 +0000 GET / HTTP/1.1 200 {\x22Host\x22:\x22localhost:8899\x22,\x22User-Agent\x22:\x22curl/7.64.1\x22,\x22Accept\x22:\x22*/*\x22,\x22H\x22:\x22First,Second\x22}
您可以在 log_format 指令中使用转义参数来更改转义的应用方式。
escape=json 输出示例:
log_format main escape=json ...
{\"Host\":\"localhost:8899\",\"User-Agent\":\"curl/7.64.1\",\"Accept\":\"*/*\",\"H\":\"First,Second\"}
另一种选择是在javascript函数中用base64编码包装json:
function headers_json_base64(r) {
return JSON.stringify(r.headersIn).toString('base64')
}
使用 njs,您可以在 javascript 函数中使用 ngx.log
或 r.log
(对于旧版本的 njs ngx 对象不可用)来记录标头。应该显式调用 js 函数以使其工作,例如
js_header_filter 指令。
js 模块:
function headers_json_log(r) {
return ngx.log(ngx.WARN, JSON.stringify(r.headersIn))
}
export default {headers_json_log};
启用登录位置:
location /log/ {
js_header_filter headers.headers_json_log;
return 200;
}
因为没有应用错误日志转义,所以你会得到原始的json:
2021/04/16 12:22:53 [warn] 24#24: *1 js: {"Host":"localhost:8899","User-Agent":"curl/7.64.1","Accept":"*/*","H":"First,Second"}
如果您不想弄乱访问日志或只需要记录特定位置的标头(对于特定位置 access_log 指令和单独的 log_format 也可以使用),记录到错误日志可能很有用
mirror 指令可用于将请求镜像到不同的位置。它可能比 tcpdump 更方便,尤其是当上游流量被加密并且比使用 njs 更简单时。
mirror 只能用于捕获请求头,因为响应头是独立返回的。
mirror 指令可用于服务器范围内的 http 和服务器上下文或位置上下文。
# using mirror in server context
mirror /mirror;
mirror_request_body off;
location /mirror {
# Use internal so that location is not available for direct requests
internal;
# Use small timeout not to wait for replies (this is not necessary)
proxy_read_timeout 1;
# Pass headers to logging server
proxy_pass http://127.0.0.1:6677;
# send original request uri in special header
proxy_set_header X-Original-URI $request_uri;
}
要记录标头,可以使用简单的 http 服务器或仅使用 netcat oneliner:
nc -kl 6677 > /path/to/headers.log
因为netcat不回复nginx,nginx会用超时错误填充错误日志,这个错误不会影响客户端。
答案 3 :(得分:1)
基于@ user1778602的响应,可以使用set_by_lua将所有标头设置为一个变量,以便以后在log_format处使用。以下设置变量“ $ request_headers”中的所有标头
set_by_lua $request_headers '
local h = ngx.req.get_headers()
local request_headers_all = ""
for k, v in pairs(h) do
request_headers_all = request_headers_all .. ""..k..": "..v..";"
end
return request_headers_all
';