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

gau*_*nix 30 logging nginx

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

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

我会接受hacky解决方案!

pet*_*etr 40

还有两个选项可以在 nginx 中记录标头。

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

使用 njs 记录请求和响应标头

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

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

load_module modules/ngx_http_js_module.so;
Run Code Online (Sandbox Code Playgroud)

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

使用njs记录访问日志

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

HTTP 请求对象具有键值格式的headersInheadersOut字段,重复的标头将合并在此字段中,而rawHeadersInrawHeadersOut是原始标头数组的数组。

创建js模块,使用json序列化header:

// /etc/nginx/headers.js
function headers_json(r) {
  return JSON.stringify(r.headersIn)
}

export default {headers_json};
Run Code Online (Sandbox Code Playgroud)

导入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';
Run Code Online (Sandbox Code Playgroud)

访问日志中的转义

默认情况下,访问日志中的字符串会被转义,因此您将得到如下内容:

 # 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}
Run Code Online (Sandbox Code Playgroud)

您可以在log_format指令中使用 escape 参数来更改转义的应用方式。

escape=json 输出示例:

log_format main escape=json ...
{\"Host\":\"localhost:8899\",\"User-Agent\":\"curl/7.64.1\",\"Accept\":\"*/*\",\"H\":\"First,Second\"}
Run Code Online (Sandbox Code Playgroud)

另一种选择是在 javascript 函数中以 Base64 编码包装 json:

function headers_json_base64(r) {
  return JSON.stringify(r.headersIn).toString('base64')
}
Run Code Online (Sandbox Code Playgroud)

使用 njs 记录到错误日志

使用 njs,您可以在 javascript 函数中使用ngx.logor 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};
Run Code Online (Sandbox Code Playgroud)

启用登录位置:

location /log/ {
  js_header_filter headers.headers_json_log;
  return 200;
}
Run Code Online (Sandbox Code Playgroud)

对于错误日志,不应用转义,因此您将获得原始 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"}
Run Code Online (Sandbox Code Playgroud)

如果您不想弄乱访问日志或者只需要记录特定位置的标头(对于特定位置也可以使用 access_log 指令和单独的 log_format ),则记录到错误日志可能会很有用

使用镜像指令记录请求标头

镜像指令可用于将请求镜像到不同的位置。它可能比 tcpdump 更方便,尤其是当上游流量被加密时,并且比使用 njs 更简单。

镜像只能用于捕获请求标头,因为响应标头是独立返回的。

镜像指令可以在 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;
}
Run Code Online (Sandbox Code Playgroud)

要记录标头,可以使用简单的 http 服务器或 netcat oneliner:

nc -kl 6677 > /path/to/headers.log
Run Code Online (Sandbox Code Playgroud)

由于netcat不回复nginx,nginx会用超时错误填充错误日志,此错误不会影响客户端。


Toz*_*art 23

仅对于请求标头,它可以在 nginx/1.18.0 上开箱即用。

  1. 编辑/etc/nginx/nginx.conferror_log /var/log/nginx/error.log;. 将其移到 http/server 范围之外,使其看起来像:
error_log /var/log/nginx/error.log debug;

http {
  ...
Run Code Online (Sandbox Code Playgroud)

注意这个debug部分!

  1. 重新启动服务器,然后执行tail -f /var/log/nginx/error.log | grep "http header"

输出示例:

2022/04/06 11:53:55 [debug] 3930380#3930380: *5 http header: "Host: example.org"
2022/04/06 11:53:55 [debug] 3930380#3930380: *5 http header: "Connection: Keep-Alive"
2022/04/06 11:53:55 [debug] 3930380#3930380: *5 http header: "X-Forwarded-For: 127.0.0.1"
2022/04/06 11:53:55 [debug] 3930380#3930380: *5 http header: "X-Forwarded-Proto: https"
2022/04/06 11:53:55 [debug] 3930380#3930380: *5 http header: "user-agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0"
2022/04/06 11:53:55 [debug] 3930380#3930380: *5 http header: "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
...
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你!对于从 docker 容器运行 nginx 的任何阅读本文的人来说,(https://hub.docker.com/_/nginx) 的“在调试模式下运行 nginx”部分中的说明将会有所帮助。本质上,您需要运行“nginx-debug”二进制文件而不是“nginx”二进制文件才能使调试模式正常工作 (3认同)

gau*_*nix 15

经过大量研究,我可以得出结论,开箱即用是不可能的.

更新 - 您可以使用Lua附带的openresty.使用Lua可以做很酷的事情,包括记录所有标题,Redis或其他服务器

  • 下面有一些答案可以使用库存 nginx 开箱即用 - 请使用调试模式下的错误日志查看 @Tozzart 的答案。 (2认同)

use*_*602 13

正如@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
}
Run Code Online (Sandbox Code Playgroud)

检查您的错误日志.

  • 一个更有用的变体是将它们注入到您可以在您的“log_format custom”中使用的 nginx var 中 - 否则您将不得不尝试将随机标头日志与特定请求相关联,即使是在服务器慢.. :)(如果我设置了这样的东西,我会分享它) (3认同)

Ama*_*Bah 11

基于@user1778602 的响应,set_by_lua_block可用于将所有标头设置为稍后在 log_format 中使用的变量(请参阅此答案中的更多详细信息)。

set_by_lua_block $request_headers{
  local h = ngx.req.get_headers()
  local request_headers_all = ""
  for k, v in pairs(h) do
    local rowtext = ""
    rowtext = string.format("[%s %s]\n", k, v)
    request_headers_all = request_headers_all .. rowtext

  end
  return request_headers_all
}
Run Code Online (Sandbox Code Playgroud)

更新您的log_format(请务必在 eg 中使用新格式access_log):

log_format myformat escape=none "$request_headers"
Run Code Online (Sandbox Code Playgroud)

PS:注意记录 PII 数据可能违反 GDPR :)

  • 作为旁注,我的标题没有出现,因为它包含下划线。我必须启用此指令才能使其工作: enable-underscores-in-headers https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md#启用标题中的下划线 (3认同)