站点接上CDN,或者前面挂了一层负载均衡之后,nginx的access.log里remote_addr那一列会变成回源节点的地址。日志里所有访客看起来都从同几个IP过来,按IP做的限流、封禁、地域统计一起失效,防爬规则也拦不准。
要改这件事,用的是ngx_http_realip_module。这个模块不是默认编译进去的,编译参数里得带上--with-http_realip_module。用包管理器装的官方包一般已经包含,nginx -V的输出里能看到这一项,看不到就得换包或者自己编一个,光改配置是没用的。
三个指令配合起来才管用,set_real_ip_from写上可信的地址段,CDN厂商会给一份回源IP清单,把那几段填进去;real_ip_header指定从哪个头里取地址,默认值是X-Real-IP,不是多数人以为的X-Forwarded-For;real_ip_recursive决定取头里的哪一个,默认off。三项少配一项,取到的地址就不对,而且错得很安静,日志照常写,只是写的是错的。其中最容易配错的是real_ip_recursive,关着的时候,只要来源地址落在可信段里,nginx就直接取请求头里的最后一个地址;打开之后,它从后往前跳过所有可信地址,取第一个不可信的。前面挂了两层代理的情况,关着取到的是中间那层的地址,打开才能落到真正的访客身上。用CDN加负载均衡的站点,基本都是要打开的。
配置写出来大概是这样,先set_real_ip_from跟上可信段,再real_ip_header指向X-Forwarded-For,最后real_ip_recursive on,写完nginx -t过了再reload。生效之后remote_addr变成访客地址,原始地址还留在$realip_remote_addr里,日志格式里两个都打上,出问题的时候能对得回去。改之前先看一眼现有的log_format,新加列会让下游的日志解析脚本认不出来。字段位置一变,统计就全乱。
set_real_ip_from只填CDN公布的回源段,CDN换段要跟着改;写成0.0.0.0/0就等于告诉nginx谁发来的头都认,任何人自己加一个X-Forwarded-For就能把地址写成任意值,限流形同虚设,日志也彻底失去参考价值,这种情况下信任范围配宽比不配还糟。这一处漏填会表现为部分请求取不到真实地址,看上去像是随机失效,查起来很费时间。
验证的办法是从公网发起一次请求,看日志里打出来的地址是不是自己这条线路的出口IP。再用curl带一个自造的X-Forwarded-For头请求一次,看日志里会不会出现那个自造地址。出现了而这条请求并不是从那儿发出的,就说明信任范围配宽了,正常配置下这个伪造的头应该被忽略掉。取到真实地址之后,之前按回源IP写的那批规则要一并重看,不少会突然不再触发,限流从几乎不生效变成真的按访客算,阈值也得跟着调。
申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!

