如何捕获Google Maps API访问KML文件的所有IP地址
问题背景
我刚接触Google Maps API与KML处理,在网站上通过API展示服务器上的KML文件,希望捕获所有访问KML文件的客户端IP,但发现Apache日志遗漏了部分IP。
最初的解决方案(.htaccess + access.php)
配置.htaccess将未携带指定Cookie的请求转发到access.php:
RewriteEngine On RewriteCond %{HTTP_COOKIE} !fla=[^;]+ [NC] RewriteRule ^(.*)$ access.php?url=https://%{HTTP_HOST}%{REQUEST_URI} [L,QSA]
access.php负责记录IP、设置Cookie并跳转至目标KML:
<?php $file = fopen('ips.txt', 'a+'); fwrite($file, $_SERVER['REMOTE_ADDR'] . "\n"); fclose($file); setcookie("fla", hash("sha512", random_bytes(10)), time() + 30, "", "", 1); header("Location: ". $_GET['url']); ?>
测试发现:客户端直接访问KML文件时能正常捕获IP,但Google Maps API仅在首次解析KML时记录IP,后续加载KML无法捕获Google的源IP,只有重命名KML文件名后才会再次记录。
优化后的方案(servefile.php)
将逻辑整合到单个servefile.php,增加X-Forwarded-For检测,移除.htaccess和access.php:
<?php header("X-Frame-Options: deny"); header("X-XSS-Protection: 1; mode=block"); header("Strict-Transport-Security: Max-Age=31536000; includeSubDomains"); header("X-Content-Type-Options: nosniff"); header("Cache-Control: no-cache, no-store, must-revalidate"); header("Pragma: no-cache"); header("Referrer-Policy: no-referrer"); $ip_file = fopen('ips.txt', 'a+'); if (isset($_SERVER['HTTP_CLIENT_IP']) && array_key_exists('HTTP_CLIENT_IP', $_SERVER)) { $ip_addr = $_SERVER['HTTP_CLIENT_IP']; } else if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) { $x_fwd_ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $x_fwd_ips = array_map('trim', $x_fwd_ips); $ip_addr = $x_fwd_ips[0]; } else { $ip_addr = $_SERVER['REMOTE_ADDR']; } fwrite($ip_file, $ip_addr . "\n"); fclose($ip_file); // redacted for security reasons; it is present in the live code $base_url = ""; $file = $base_url . "" . $_GET['url']; $filename = $_GET['url']; header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Transfer-Encoding: binary'); header('Accept-Ranges: bytes'); @readfile($file); ?>
问题依旧:客户端直接访问文件时能捕获IP,而加载Google地图时仅首次加载KML能捕获IP。
核心原因
Google Maps API会缓存已解析的KML资源,后续请求直接从Google的CDN或缓存服务器读取,不会再向你的服务器发起请求,因此无法捕获后续的IP。重命名KML相当于生成新的资源URL,会触发Google重新请求,所以能再次记录IP。
解决方法
1. 给KML请求添加动态参数(最有效)
在调用Google Maps API加载KML时,为URL添加唯一动态参数(比如时间戳、随机字符串),让每次请求的URL都不同,绕过Google的缓存。
- 示例:将KML加载URL从
https://your-server.com/your.kml修改为:// 前端或后端生成带时间戳的URL $kmlUrl = "https://your-server.com/servefile.php?url=your.kml&t=" . time(); - 每次请求的URL都是唯一的,Google会重新向你的服务器发起请求,确保每次访问都能被记录IP。
2. 强化缓存控制响应头
虽然已设置no-cache类头,但Google的缓存机制可能忽略这些指令,可进一步优化:
修改servefile.php中的缓存头部分:
header("Cache-Control: private, no-cache, no-store, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); // 设置过期时间为过去,强制不缓存
private指令明确告诉缓存服务器(包括Google的)该资源为私有,不能共享缓存。
3. 前端主动触发KML刷新(配合地图逻辑)
如果需要在特定场景下(比如地图刷新、内容更新)重新加载KML,可在前端调用Google Maps的fetchKml()方法,同时携带新的动态参数,确保每次请求都触发服务器记录。
4. 区分Google服务器IP与客户端真实IP
注意:Google Maps API请求KML时,发起请求的IP是Google服务器的IP,而非客户端真实IP。若要捕获客户端真实IP,需通过前端上报:
- 在页面加载时,用JavaScript获取客户端IP(可通过后端接口配合),然后发送到你的服务器记录。
内容的提问来源于stack exchange,提问作者nbdun24

