准确提取nginx日志中客户端真实ip并统计地理归属,需三步:配置set_real_ip_from信任代理、用geolite2离线库查城市级位置、用awk+python聚合统计;注意ipv6解析、私有地址过滤及隐私合规。

提取 Nginx 日志中的 IP 地址并做地理位置归属统计,核心是三步:解析日志获取客户端真实 IP、用 IP 库查归属地、聚合统计。关键在于准确提取 IP(尤其经过代理时),以及选择轻量可靠的地理库。
准确提取客户端真实 IP
Nginx 日志中 $remote_addr 默认记录的是直连客户端(可能是代理或 CDN 节点),不是用户真实 IP。要拿到真实 IP,需结合 X-Forwarded-For 或 X-Real-IP 头,并在配置中信任可信代理:
- 确保 Nginx 配置中已启用
set_real_ip_from指令,明确列出你信任的 CDN 或反向代理网段(如 Cloudflare、公司内部 LB) - 使用
real_ip_header X-Forwarded-For;或real_ip_header X-Real-IP;,并开启real_ip_recursive on;,让 Nginx 正确截取最左/最右的有效 IP - 日志格式中建议直接记录
$remote_addr(此时已是真实 IP),避免后期从$http_x_forwarded_for字符串里正则提取——易出错(如伪造头、多层逗号分隔)
用离线 IP 库做地理查询(推荐 GeoLite2)
不依赖网络 API,稳定且适合批量处理。GeoLite2 Free(需注册下载)支持 IPv4/IPv6,精度达城市级:
- 下载
GeoLite2-City.mmdb到服务器(如/usr/share/GeoIP/GeoLite2-City.mmdb) - 用命令行工具
mmdblookup快速验证:mmdblookup --file GeoLite2-City.mmdb --ip 8.8.8.8 city names en - Python 中推荐用
geoip2库:pip install geoip2,加载 mmdb 后可查 country、city、longitude、latitude 等字段
用 awk + Python 快速完成统计(示例流程)
假设日志格式为:log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
- 先用
awk提取所有 IP(去重可选):awk '{print $1}' access.log | sort | uniq > ips.txt - 写一个 Python 脚本读取
ips.txt,逐行查 GeoLite2,输出IP, country_name, city_name - 再用
awk或sort | uniq -c统计国家/城市频次:awk -F, '{print $2}' ip_geo.csv | sort | uniq -c | sort -nr - 进阶可导出为 CSV,用 Pandas 做热力图或 Top10 表格,或导入 Elasticsearch + Kibana 可视化
注意事项与避坑点
真实场景中容易忽略的细节:
-
IPv6 支持:确认所用 IP 库和代码能处理 IPv6;
awk '{print $1}'对 IPv6 日志可能失效(因含冒号和空格),改用awk '{match($0, /^[^ ]+/); print substr($0, RSTART, RLENGTH)}' - 私有地址过滤:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、127.0.0.1 等无需查库,脚本中应跳过
-
日志时区与滚动:统计前确认日志时间范围,避免跨天文件漏读;gzip 压缩日志可用
zcat替代cat -
隐私合规:部分地区要求匿名化处理 IP(如保留前两段),查地理时可用
ipaddress.ip_network(ip + '/24', strict=False)归一化后查询











