如何在nginx中记录所有标头?

时间:2014-06-24 06:51:38

标签: logging nginx

根据这个问题的标题,我如何记录客户端浏览器在Nginx中发送的所有标头?我还想记录响应头。请注意,我使用nginx作为反向代理。

完成文档后,我了解到我可以记录一个特定的标题,但我想记录所有的标题。

我会接受hacky解决方案!

4 个答案:

答案 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 中记录标头。

  • nginx njs 可以使用脚本语言代替 lua(njs 可能被认为更易于安装和使用,并且更“官方”)
  • mirror 指令(仅用于请求标头)

使用 njs 记录请求和响应头

njs 可以是来自包存储库的 installed 并且预安装在官方 nginx docker 镜像中。 njs 至少从 1.9.15 版本的 nginx(相当旧)开始可用,但最好使用更新的版本。

安装后在nginx配置中启用njs http模块:

load_module modules/ngx_http_js_module.so;

标题可能会记录到错误或访问日志中。

使用njs登录访问日志

  • 决定使用哪种格式(JSON、自定义、base64...)
  • 创建具有将标题结构转换为字符串的功能的 js 模块(约 3 行代码)
  • 在 nginx 配置中导入这个 js 模块(~1 行)
  • 声明一个变量用于 log_format 指令(~1 行)
  • 将此变量添加到日志格式(~1 行)

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 记录到错误日志

使用 njs,您可以在 javascript 函数中使用 ngx.logr.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
';