
简介这是一套基于PHP开发的IP探针程序源码包面向网站开发者、运维人员及对IP定位技术感兴趣的初学者。通过部署该程序可获取访问者IP地址并结合IP库实现查询与地理位置定位生成专属链接分享给好友后即可查看对方所在城市适用于网站访问统计、趣味位置互动、安全风控等场景。压缩包共156个文件大小约715KB其中包含40个PHP核心逻辑文件、2个SQL数据库脚本以及用于界面展示的CSS、JS和GIF图标等前端资源目录结构清晰便于按模块阅读与调试。程序内置IP解析与地理转换模块支持自定义配置和二次开发可作为学习PHP网络编程、IP库API接入及前后端交互的实用范例。已有4631人学习下载适合想快速搭建IP探针服务或研究IP定位原理的开发者参考。 搞IP探针这个东西最开始其实是我自己网站被刷恶心了想看看访客到底从哪来、是真人还是脚本。后来做着做着发现“IP探针”这四个字背后藏着一整条链路拿到访客的出口IP只是第一步后面的归属地解析、前端定位、风险判断才是真正花时间的地方。这篇文章就把我从零搭一个IP探针到落地使用的完整过程拆开讲包括技术选型思路、核心代码、遇到的坑和排查技巧适合那些想给自己的站点加个访客画像功能、或者做网络诊断和风控识别的开发者参考。1. 拆解IP探针的核心链路拿到IP只是一个开始1.1 探针到底在探测什么很多人以为IP探针就是“记录一下访客IP”实际上一次完整的IP探针动作能从一次普通的网页访问里榨出不少信息来源IP访客请求到达服务器时TCP连接对端地址也就是出口IP。请求头信息User-Agent、Accept-Language、Referer这些能侧面反映设备类型和浏览器环境。时区与语言JavaScript可以拿到访客本地时区、系统语言这些信息和IP归属地交叉验证很有用。前端定位结果浏览器Geolocation API在用户授权后可以返回经纬度精确度比IP定位高一个量级。单看每一项都挺普通但把这些信息叠在一起就能形成一张“访客画像”。比如一个IP归属地在上海、浏览器语言却是繁体中文、系统时区在洛杉矶、前端定位却指向深圳的请求基本可以断定是用了代理或虚拟定位这类信号在风控场景里就是关键特征。1.2 选型思路离线库在线接口双轨在技术选型上我一开始直接调在线IP查询API用了一阵子发现两个痛点一是免费接口限流严重流量稍大就返回失败二是每次请求都把访客IP发给第三方站点访问数据等于裸奔。后来我切换成了“离线库为主、在线接口兜底”的双轨方案。方案精确度响应速度成本离线可用离线IP库ip2region城市级部分到区县毫秒级本地查询免费开源完全可用在线查询API区县级动态更新100-300ms网络开销免费有限流/付费不可用以ip2region为例它把IP段和地理位置映射关系预生成到一个二进制或内存文件中查询时用二分查找单次查询耗时在微秒到毫秒级别完全不影响主业务流程。覆盖面方面国内IP段基本都能命中城市和运营商国外IP也能查到国家和大区。唯一的缺点是数据是静态快照新分配的IP段可能要等库更新才能正确识别所以在线接口做兜底专门处理离线库查不到的场景。2. 服务端实现如何拿到访客的真实出口IP2.1 直接解析RemoteAddr的坑刚开始我图省事后端起个服务直接取请求的RemoteAddr本地测试没问题一上线就发现拿到的全是内网IP。原因很简单我的服务跑在Nginx反代后面RemoteAddr是Nginx的地址不是访客的。要拿真实出口IP得处理代理链。常规做法是看X-Forwarded-For和X-Real-IP这两个头但这里有个大坑这两个头是客户端可以伪造的。如果服务直接信任这两个头攻击者随手发一个X-Forwarded-For: 8.8.8.8就能把自己的真实IP伪装掉。# Python Flask示例取真实出口IP from flask import Flask, request app Flask(__name__) def get_real_ip(): # 实际情况中你需要在Nginx层配置只信任来自Nginx的回源请求 xff request.headers.get(X-Forwarded-For, ) if xff: # XFF格式: client, proxy1, proxy2 # 取第一个但该值可能被伪造 ip xff.split(,)[0].strip() else: ip request.remote_addr return ip正确做法是在Nginx这一层就把可信的访客IP写到自定义头里业务后端只读取这一层解析好的结果# Nginx配置示例经过多层代理后设置可信IP server { listen 80; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://backend_upstream; } }这样后端拿到的X-Real-IP就是Nginx直接建立的TCP连接对端地址在没有额外可信代理层的情况下这个值就是访客出口IP。2.2 出口IP查询与归属地解析落地拿到IP后下一步就是归属地解析。我用的是Java版本的ip2region查询接口极其简单// Java示例ip2region离线IP库使用 import org.lionsoul.ip2region.xdb.Searcher; import java.io.IOException; public class IpRegionHelper { // 加载离线xdb文件 private static Searcher searcher; static { try { searcher Searcher.newWithFileOnly(ip2region.xdb); } catch (IOException e) { e.printStackTrace(); } } public static String search(String ip) throws Exception { if (searcher null) { return unknown; } // 返回格式国家|区域|省份|城市|ISP return searcher.search(ip); } }返回的格式大概是中国|0|广东省|深圳市|电信把它按竖线拆开就能入库或者展示。注意这里的“区域”字段很多情况下是0不要把它和“省份”弄混。离线库查不到的情况下走在线接口兜底# 在线接口兜底示例 curl https://api.vore.top/api/IPdata?ip1.2.3.4这里要提醒一下在线接口返回的字段结构千差万别有的给province有的给region有的嵌套在data里。代码里对响应做字段归一化处理避免下游逻辑频繁改动。2.3 记录和管理探针数据探针不仅要实时返回IP信息还得把历史数据存下来方便后续分析。我设计了一张简单的访问记录表CREATE TABLE ip_probe_log ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, ip VARCHAR(45) NOT NULL, country VARCHAR(64), province VARCHAR(64), city VARCHAR(64), isp VARCHAR(64), user_agent VARCHAR(512), lang VARCHAR(64), timezone VARCHAR(64), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_created_at (created_at), INDEX idx_ip (ip) );写入时要注意两点一是User-Agent字段设长一点有些爬虫的UA能到四五百字符二是对IP字段加索引后续做“查询某个IP的所有访问记录”会快很多。日志量大的时候建议做表分区或者定期归档避免单表膨胀后查询变慢。3. 前端定位从IP到物理位置的最后一公里3.1 HTML5 Geolocation与IP定位的配合IP定位的精度通常在市级对“访客大概在哪个城市”这个场景够用但如果你想做更精细的定位比如门店附近推荐、本地化内容分发就必须依赖前端定位能力。HTML5 Geolocation API的基本用法很简单// 前端定位示例 function getLocation() { return new Promise((resolve, reject) { if (!navigator.geolocation) { reject(new Error(当前浏览器不支持定位)); return; } navigator.geolocation.getCurrentPosition( (position) { const { latitude, longitude, accuracy } position.coords; // accuracy是定位精度单位米数值越小越准 resolve({ lat: latitude, lng: longitude, accuracy }); }, (error) { // 用户拒绝或定位失败 reject(error); }, { enableHighAccuracy: true, timeout: 5000, maximumAge: 0 } ); }); }这个方案有一个硬性前提必须在HTTPS环境下运行HTTP页面会被浏览器直接拦截定位权限。另一个现实问题是用户授权率。实测下来让用户主动点“允许定位”的转化率并不高很多人看到权限弹窗直接拒绝。所以我的做法是IP定位作为默认值前端定位作为增强项。先展示IP定位的城市如果用户授权了前端定位就再细微校正一下并标明两者精度差异。3.2 虚拟定位如何影响“定位”可靠性既然前端定位能做就有人会动歪心思改定位、虚拟定位把自己伪装成另一个位置。从构建探针服务的角度看这其实成了一个需要识别的风险特征。识别虚拟定位我总结了几个比较有效的信号经纬度与IP归属地偏差前端定位返回的坐标和IP归属城市差了几百公里。正常用户不会出现这种矛盾。定位精度异常真机定位精度通常在10-100米虚拟定位有些返回的精度值非常标准比如稳定的65米反而可疑。速度异常两次请求间隔几分钟坐标却从北京跳到了上海人不可能这么快高德、百度地图的围栏判定也有类似逻辑。传感器数据缺失真机在Geolocation调用时往往伴随加速度计、陀螺仪等传感器数据虚拟定位环境通常给不出这些数据。这些特征单看哪一个都不够实锤但综合起来置信度就高了。做风控系统时可以把这些特征做成评分项超过阈值就标记为风险访问。3.3 局域网IP探测与设备发现探针不只是公网侧的工具内网环境里一样用得上。比如排查网络问题想看看局域网里有哪些设备、有没有陌生的IP接入了网络扫一圈就清楚了。Linux环境下我常用arp-scan# 扫描当前网段下的所有活跃设备 sudo arp-scan --localnet输出会列出每个IP对应的MAC地址和厂商信息通过MAC前缀基本能判断设备类型手机、电脑、摄像头、路由器。如果发现不明设备那大概率是有人蹭网或者有IoT设备被悄悄接进来了。跨网段或者要识别操作系统的话用nmap更合适# 快速扫描指定网段识别开放端口和操作系统指纹 nmap -sn 192.168.1.0/24 nmap -O 192.168.1.1-sn是跳过端口扫描只做主机发现速度快适合日常巡检-O是操作系统指纹识别更重一些但能帮助你识别设备类型。内网IP冲突排查也常用这个思路先扫一遍再对比冲突的IP对应哪些MAC基本能锁定是哪台设备的问题。4. 进阶排查握手细节、Wireshark抓包与DNS反查4.1 Wireshark按IP反查域名解析记录有时候探针发现一个IP频繁访问某个敏感接口你想知道这个IP背后到底是什么业务直接反查域名解析记录是个好办法。Wireshark里抓包后如果这个IP做过DNS解析很多应用会先做DNS解析再连接可以用过滤表达式快速定位dns.resp.type 1 dns.a 1.2.3.4这条过滤会把所有DNS响应中包含1.2.3.4的数据包列出来前面就有对应的查询域名。如果这台机器长期访问某个域名这个反查基本能看出来。命令行环境下的处理更直接# 反查域名解析记录基于DNS不保证一定存在PTR记录 dig -x 1.2.3.4 nslookup 1.2.3.4要说明一点dig -x查的是PTR反向记录很多IP地址并没有配置PTR特别是家用宽带的动态IP查不到是常态。这种情况下只能靠流量分析或者IP信誉库辅助判断。4.2 Ping延迟与多节点探测辅助定位Ping延迟虽然不能精确到城市但能给出一个距离量级的参考。从国内到美西的延迟通常在150ms左右到欧洲要250ms以上如果探针显示IP归属地是北京但Ping延迟只有10ms说明物理链路确实很近归属地可信度高。更系统的做法是结合多节点探测比如你是做内容分发的想知道某个访客IP最适合连哪个节点就可以让访客分别Ping不同节点取延迟最低的# 测量到多个节点的延迟 ping -c 4 cn-node1.example.com ping -c 4 us-node1.example.com ping -c 4 sg-node1.example.com实际项目中我不会用传统ICMP Ping而是通过HTTP请求的时间来模拟因为很多网络环境禁ICMP而且HTTP的TCP握手时间更贴近真实业务路径。前端发起几个极小的HTTP探测请求记录响应时间就能选出一个最优节点。这个思路在做GSLB全局负载均衡或边缘节点调度时非常实用。4.3 风险IP与黑rom设备识别思路黑rom设备这个词在风控圈出现频率很高指那些刷了非官方系统的安卓设备。这类设备由于系统被篡改很多安全检测特征失效导致设备指纹不可信。IP探针在识别这类风险时能贡献的信息主要是IP维度的这个IP对应的运营商是不是经常被用来跑脚本的IDC段。同一个IP下设备指纹的变动频率是不是异常高正常家庭宽带下设备相对固定。该IP是否命中公开的威胁情报库比如被标记为爬虫、扫描、恶意下载等行为。一个简单但有效的做法是维护一个“IP信誉评分表”维度低风险0-3分中风险4-6分高风险7-10分归属类型家庭宽带移动网络IDC机房/VPS每日指纹变化数1-3个4-10个10个以上历史威胁情报命中无记录偶尔命中频繁命中访问行为规律规律性弱定时任务模式高频并发请求把IP维度、设备指纹维度和行为维度三块分数加权汇总超过阈值就触发二次验证或者直接拦截。这块没有一劳永逸的方案信誉表需要持续更新我自己的做法是每天拉一次公开的威胁情报合并到离线库里供线上查询。5. 常见问题与排查技巧实录5.1 常见问题速查表实际操作中踩过的坑整理成一张速查表方便照着排查问题现象可能原因解决方案拿到的IP全是内网IP服务在Nginx/CDN后未透传源IP检查是否有反代层在反代层配置proxy_set_header X-Real-IP $remote_addr归属地精度差使用的IP库版本过旧定期更新ip2region库文件至少每季度一次前端定位直接报错页面运行在HTTP而非HTTPS配置HTTPS证书Geolocation API在非安全上下文不可用用户授权率低定位非用户主动场景在用户点击“查看附近门店”等主动操作后再发起定位请求IP解析有时返回unknown离线库未收录该IP段代码增加在线接口兜底逻辑同一个IP归属地频繁变动访问来自运营商动态IP池属于正常现象结合端口号和UA做关联分析更可靠5.2 踩坑实录请求头伪造与代理链陷阱真想跑起来坑主要在请求头处理上。我一度因为图省事直接读X-Forwarded-For的第一个值结果被一个懂行的老哥用伪造头刷了一晚上“来自美国总统府的访问”。后来改成了“取最后一个非信任代理IP”的逻辑并在Nginx层封掉了客户端直接传X-Forwarded-For的口子。另一个容易忽略的点是IPv6。很多统计系统只处理了IPv4IPv6地址一进来直接截断或解析失败。IPv6地址最长45个字符数据库字段容量一定要留够而且IPv6的归属地库和IPv4是分开的接入时要分别验证。5.3 数据库查询慢与动态IP的隐患日志量大了之后查询变慢是必然的。我的做法是对created_at做分区按月分区基本能满足需求配合IP索引按IP查历史记录也能保持在百毫秒内。动态IP的问题更隐蔽同一个IP今天归A城市、明天归B城市因为运营商把IP池重新分配了。这种情况靠IP定位是不准的只能通过设备指纹、Cookie、登录态等维度做关联把“人和设备”而非“IP”作为长期身份锚点。另外如果你在做探针时考虑了在线接口兜底建议加一层缓存。每次查询前先查本地缓存Redis或者内存Map查不到再请求在线接口避免同一个IP被反复询问既省流量又降低被限流的概率。缓存过期时间我一般设24小时因为IP归属地变化不会太频繁24小时足够。最后一点实际体会写代码处理IP定位的时候最大的心魔是“想要精确再精确”。但现实是如果你想在公网上定位一个用户IP最多给你一个城市前端定位也常常拿不到授权能稳定拿到几十公里级别的结果就已经足够支撑大多数业务场景了。与其死磕单个IP的定位精度不如把精力花在数据交叉验证和异常识别上这套东西真正跑起来价值比一个“完美”的IP库大得多。本文还有配套的精品资源点击获取