Nginx中透传客户端真实IP的技巧
作者:小小工匠
概述
1. 为什么需要获取客户端的真实 IP 地址?
在使用 Nginx 作为反向代理服务器时,默认情况下,后端服务器只能看到 Nginx 的IP地址。为了记录日志、限制访问或进行其他基于 IP 地址的操作,获取客户端的真实 IP 地址非常重要。
2. Nginx 中用于获取真实 IP 地址的模块
Nginx 提供了两个主要模块来处理这一需求:
- HttpRealipModule: 用于从请求头中提取客户端的真实 IP 地址。
- HttpGeoipModule: 用于根据 IP 地址定位地理位置(较少用于获取真实 IP)。
这里主要介绍 HttpRealipModule
。
3. 配置示例和步骤
3.1 安装和启用模块
大多数情况下,Nginx 已经包含了 HttpRealipModule
。可以通过以下命令检查:
nginx -V 2>&1 | grep -o with-http_realip_module
如果没有启用该模块,则需要重新编译 Nginx 或安装包含该模块的 Nginx 版本。
3.2 配置 Nginx
编辑你的 Nginx 配置文件(通常位于 /etc/nginx/nginx.conf
或 /etc/nginx/conf.d/
中的某个文件),添加以下配置:
http { ... set_real_ip_from 0.0.0.0/0; # 允许所有 IP 地址的代理 real_ip_header X-Forwarded-For; real_ip_recursive on; ... server { ... location / { ... # 如果需要日志中记录真实 IP log_format custom '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log custom; ... } } }
3.3 配置说明
set_real_ip_from
: 允许哪些 IP 地址可以作为可信代理。如果你的代理服务器在特定的 IP 范围内,只允许那些 IP。real_ip_header
: 指定哪个头部字段包含了真实 IP 地址。常用的是X-Forwarded-For
。real_ip_recursive
: 递归地检查X-Forwarded-For
头部中的所有 IP 地址,直到找到第一个非可信代理的 IP。
4. 潜在的陷阱和调试方法
4.1 潜在的陷阱
- 代理链中的 IP 地址顺序:
X-Forwarded-For
头部可能包含多个 IP 地址,代表了一系列代理服务器。确保配置的real_ip_recursive on
正确地获取到第一个客户端的 IP 地址。 - 安全问题:配置
set_real_ip_from
时,要小心不要信任不受控制的 IP 地址,否则可能会导致 IP 欺骗。
4.2 调试方法
- 检查日志:通过查看 Nginx 日志,确认是否成功获取到真实 IP 地址。
- 测试请求:使用工具如
curl
模拟请求,带上X-Forwarded-For
头部,观察服务器的响应和日志记录。
curl -H "X-Forwarded-For: 203.0.113.195" http://your-nginx-server
实操
http 节点 添加$http_x_forwarded_for日志格式
配置文件中需要添加$http_x_forwarded_for日志格式, 核心内容如下
http { include mime.types; default_type application/octet-stream; log_format main ' $remote_addr | $http_x_forwarded_for | $remote_user | $time_local | $request | $http_host |' ' $status | $upstream_status | $body_bytes_sent | $http_referer ' ' $http_user_agent | $upstream_addr | $request_time | $upstream_response_time';
http节点 日志格式中需要添加$http_x_forwarded_for
log_format
指令用于定义 Nginx 的日志格式。它指定了在日志文件中记录哪些信息以及如何格式化这些信息。每个字段使用一个变量表示,变量之间可以用分隔符分开,如空格、竖线(|
)等。定义的日志格式可以应用于 access_log
指令,以便记录客户端请求的详细信息。
以下是 log_format
指令中各个变量的含义:
log_format main ' $remote_addr | $http_x_forwarded_for | $remote_user | $time_local | $request | $http_host |' ' $status | $upstream_status | $body_bytes_sent | $http_referer ' ' $http_user_agent | $upstream_addr | $request_time | $upstream_response_time';
$remote_addr
: 客户端的 IP 地址。这是直接连接到 Nginx 的客户端的 IP 地址。$http_x_forwarded_for
: 客户端的真实 IP 地址。通常用于记录经过代理服务器或负载均衡器的客户端 IP 地址,X-Forwarded-For
头部中包含了这些 IP。$remote_user
: 客户端的用户名,如果请求需要 HTTP 基本认证时会记录用户名。如果没有认证信息则为-
。$time_local
: 本地时间和日期,格式为day/month/year:hour:minute:second
。$request
: 请求的完整 URI,包括参数。格式为METHOD URI PROTOCOL
,例如GET /index.html HTTP/1.1
。$http_host
: 请求中的Host
头部内容,即访问的主机名。$status
: 响应的 HTTP 状态码,例如200
表示成功,404
表示未找到,500
表示服务器内部错误等。$upstream_status
: 上游服务器返回的状态码。当 Nginx 作为反向代理时,此变量记录上游服务器的响应状态码。$body_bytes_sent
: 传送给客户端的响应主体内容的字节数,不包括响应头的大小。$http_referer
: 请求的引用页面,即从哪个页面链接过来的(Referer
头部内容)。如果没有引用页面则为-
。$http_user_agent
: 客户端使用的浏览器或其他客户端的信息(User-Agent
头部内容)。$upstream_addr
: 上游服务器的地址。当 Nginx 作为反向代理时,此变量记录上游服务器的 IP 地址和端口。$request_time
: 处理请求的总时间,从接收到客户端请求到完整发送响应的时间,单位为秒。$upstream_response_time
: 从上游服务器读取响应的时间,单位为秒。
server节点 配置
server { listen 80; server_name localhost; access_log logs/access.log main; #需要添加日志引用 proxy_set_header X-Real-IP $remote_addr; #添加透传配置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for location / { root html; } }
https://www.jb51.net/server/3262802e1.htm
使用 proxy_set_header 指令设置透传头部。确保代理服务器(如 Nginx)在转发请求时保留原始客户端的 IP 地址
验证
方式一
访问Nginx页面
访问日志
192.168.0.6 | 168.138.171.206 | - | 19/May/2024:10:57:24 +0800 | GET / HTTP/1.1 | nginx.frps.fun | 200 | - | 615 | - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 | - | 0.000 | -
192.168.0.6
:
含义:直接连接到 Nginx 服务器的客户端的 IP 地址。在这个例子中,这可能是一个内网的 IP 地址。
168.138.171.206
:
含义:通过 X-Forwarded-For
头部获取的客户端的真实 IP 地址。在经过代理或负载均衡器时,这个头部会记录原始客户端的 IP 地址。
-
:
含义:客户端的用户名。在请求需要 HTTP 基本认证时记录用户名。这里没有进行认证,所以显示为 -
。
19/May/2024:10:57:24 +0800
:
含义:请求到达服务器的本地时间,格式为 day/month/year:hour:minute:second timezone
。这个例子中表示 2024 年 5 月 19 日上午 10:57:24,时区为 +0800。
GET / HTTP/1.1
:
含义:客户端的请求行,包含请求的方法(GET)、请求的资源路径(/),以及使用的 HTTP 协议版本(HTTP/1.1)。
nginx.frps.fun
:
含义:请求中的 Host
头部,表示客户端请求访问的主机名。
200
:
含义:HTTP 响应状态码,表示请求成功。200
代表成功。
-
:
含义:上游服务器的响应状态码。在没有上游服务器时,这里显示为 -
。
615
:
含义:传送给客户端的响应主体内容的字节数,不包括响应头的大小。
-
:
含义:请求的引用页面(Referer)。如果没有引用页面则显示为 -
。
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
:
含义:客户端使用的浏览器或其他客户端的信息(User-Agent)。在这个例子中,表示客户端使用的是 Chrome 浏览器,运行在 macOS 上。
-
:
含义:上游服务器的地址。在没有上游服务器时,这里显示为 -
。
0.000
:
含义:处理请求的总时间,从接收到客户端请求到完整发送响应的时间,单位为秒。
-
:
含义:从上游服务器读取响应的时间。在没有上游服务器时,这里显示为 -
。
方式二
如果我们前面没有apisix或者其它lb,也可以使用curl命令来模拟X-Forwarded-For日志
curl http://127.0.0.1/ -H 'X-Forwarded-For: 1.1.1.1' -v * About to connect() to 127.0.0.1 port 80 (#0) * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.29.0 > Host: 127.0.0.1 > Accept: */* > X-Forwarded-For: 1.1.1.1 #-v参数可以看到这里参数已经传入 > < HTTP/1.1 200 OK < Server: nginx < Date: Sun, 19 May 2024 03:09:05 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 615 < Last-Modified: Mon, 09 Oct 2023 06:03:57 GMT < Connection: keep-alive < ETag: "652397cd-267" < Accept-Ranges: bytes < <!DOCTYPE html> #http:127.0.0.1 就是Nginx服务器
- http://127.0.0.1/: 请求的 URL,127.0.0.1 表示本地服务器。
- -H ‘X-Forwarded-For: 1.1.1.1’: 添加 HTTP 请求头 X-Forwarded-For,其值为 1.1.1.1。这个头部通常用于表示客户端的真实 IP 地址。
- -v: 显示详细的请求和响应过程,包括头部信息
日志
127.0.0.1 | 1.1.1.1 | - | 19/May/2024:10:57:24 +0800 | GET / HTTP/1.1 | 127.0.0.1 | 200 | - | 615 | - | curl/7.29.0 | - | 0.000 | -
从日志文件 logs/access.log 中,可以验证 X-Forwarded-For 头部信息是否正确记录, 此日志记录显示,X-Forwarded-For 头部中传递的 1.1.1.1 已正确记录。
到此这篇关于Nginx中透传客户端真实IP的技巧的文章就介绍到这了,更多相关Nginx 透传IP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!