CDN环境下Nginx访问日志记录请求源IP

由于大部分内容分发网络(Content Delivery Network)都是使用反向代理的原理进行网站加速,这就产生了一个令人非常困惑的问题。CDN不仅隐藏了服务器/源站的IP,与此同时分布式CDN节点还代理了用户请求IP。以至于造成客户端认为CDN IP是服务器IP,服务器获取到的请求IP则全部来源于分布式CDN节点(理应如此,CDN实现了客户端对源站访问的透明)。上述问题在Nginx中可以通过自定义access_log日志来解决。

Nginx主要指令

  • log_format:用来设置日志格式;
  • access_log:用来指定日志文件的存放路径、格式(把定义的log_format
    跟在后面)和缓存大小;如果不想启用日志则access_log off ;

log_format 日志格式

  • 语法:log_format name(格式名字) 格式样式(即想要得到什么样的日志内容)

  • 1
    2
    3
    log_format cdn '$http_x_forwarded_for - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent"';
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    参数                       说明                      示例
    $remote_addr 客户端地址 123.45.67.89
    $remote_user 客户端用户名称 ---
    $time_local 访问时间和时区 13/Jul/2018:22:23:59 +0800
    $time_iso8601 ISO标准格式下的本地时间
    $request 请求的URL和HTTP协议 "GET /article.html HTTP/1.1"
    $http_host 请求地址,及输入的IP/域名 www.google.com
    $status HTTP请求状态 200
    $upstream_status upstream请求状态 200
    $body_bytes_sent 发给客户端文件内容大小 1547
    $http_referer url跳转来源
    $https_user_agent 用户终端浏览器等信息 "Mozilla/4.0 ........"
    $request_time 整个请求的总时间 0.350
    $ssl_protocol SSL协议版本 TLSv1
  • x_forwarded_for:通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_addr拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。

首先,将下列代码添加到nginx.conf文件的http模块后面,并让日志重置:

1
2
3
4
5
#自定义一个日志格式
#局限性:通过CDN的访问是用$http_x_forwarded_for来记录IP,倘若未使用CDN访问,则无法获取访问者IP。
log_format cdn '$http_x_forwarded_for - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

然后,修改nginx站点conf配置文件中的日志输出格式,修改access_log /home/wwwlogs/.log; 为access_log /home/wwwlogs/.log cdn; 即可。输入下列指令以检测Nginx配置是否正确,而后重启Nginx。

1
2
nginx -t
service nginx reload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#该方法适用于关闭CDN后,Nginx不需要变更获取IP的方法,兼容性较好。
#修改Nginx配置文件 /usr/local/nginx/conf/nginx.conf 文件,添加在 http 字段中:
map $HTTP_CF_CONNECTING_IP $clientRealIp
{
"" $remote_addr;
~^(?P[a-z0-9.:]+),?.*$ $firstAddr;
}
log_format access '$clientRealIp [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'$http_user_agent $remote_addr $request_time';
#然后再修改当前站点/usr/local/nginx/conf/vhost/*****.conf 日志记录后加上 access
access_log /www/wwwlogs/*****.log access;
#重启Nginx
service nginx reload