PHP使用cookie统计独立访客数量与实际不符问题求助
问题排查与修复方案
核心问题原因
- 爬虫过滤规则失效:原正则匹配规则过于宽泛,加入了
get、java这类普通用户UA也可能包含的关键词,同时大量小众爬虫UA没有被覆盖,导致绝大多数爬虫请求没有被过滤,直接进入统计逻辑。 - 爬虫天然不携带Cookie:爬虫每次请求都不会保存和携带Cookie,每次访问都会触发
!isset($_COOKIE['Q_zWnE8_aq'])的判断分支,若爬虫IP未被记录就会被当做新访客入库,这是统计数虚高的核心原因。 - IP获取逻辑不可靠:
HTTP_X_FORWARDED_FOR和HTTP_CLIENT_IP头可以被任意伪造,爬虫可以通过修改这两个头每次生成不同的IP标识,导致重复入库。 - 逻辑存在疏漏:原代码插入数据时用到的
$id变量未定义,且仅靠IP判断是否为老访客,很容易因为IP复用、动态IP变化出现误判。
修复方案
1. 优化爬虫过滤规则
删除正则中过于宽泛的匹配关键词,仅保留明确的爬虫相关标识,避免误判同时提升爬虫覆盖率:
// 优化后的爬虫UA匹配规则 $botPattern = '/bot|crawl|spider|googlebot|bingbot|yandex|baiduspider|360spider|sogou|curl|wget|libwww-perl|facebookexternalhit|teoma|majesticsEO/i'; if (preg_match($botPattern, $_SERVER['HTTP_USER_AGENT'])) { // 判定为爬虫,跳过统计 return; }
2. 修正IP获取逻辑
如果你的站点没有使用CDN、反向代理,直接取TCP层不可伪造的REMOTE_ADDR即可;如果使用了CDN,替换为CDN约定的真实IP头,取第一个有效IP避免伪造:
// 无代理/CDN场景直接取REMOTE_ADDR $ip = $_SERVER['REMOTE_ADDR']; // 使用CDN的场景示例,以阿里云CDN为例,取X-Forwarded-For第一个IP // $ip = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]);
3. 调整独立访客判断逻辑
新增UA字段联合IP判断,避免同IP下不同用户误判、以及爬虫伪造IP导致的重复统计,同时移除未定义的$id变量适配自增主键:
include 'configuration.php'; $botPattern = '/bot|crawl|spider|googlebot|bingbot|yandex|baiduspider|360spider|sogou|curl|wget|libwww-perl|facebookexternalhit|teoma|majesticsEO/i'; $ua = $_SERVER['HTTP_USER_AGENT'] ?? ''; // 过滤爬虫 if (preg_match($botPattern, $ua)) { return; } // 取真实IP $ip = $_SERVER['REMOTE_ADDR']; // 已有Cookie直接判定为老访客,跳过统计 if (isset($_COOKIE['Q_zWnE8_aq'])) { return; } // 新访客设置Cookie $name = 'Q_zWnE8_aq'; $value = bin2hex(random_bytes(10)); $expires = time() + 15780000; // 6个月 $path = "/"; $domain = "mydomain.in"; setcookie($name, $value, $expires, $path, $domain); // 联合IP+UA判断是否为已记录的访客,大幅降低误判概率 $sql010 = $conn2->prepare("SELECT * FROM unique_vistor WHERE ip = ? AND ua = ?"); $sql010->bind_param("ss", $ip, $ua); $sql010->execute(); $reS = $sql010->get_result()->fetch_all(MYSQLI_ASSOC); if (count($reS) == 0) { $new_visitor = 1; $result55 = $conn2->prepare("INSERT INTO unique_vistor(name,value,expires,new_visitor,ip,ua) VALUES (?,?,?,?,?,?)"); $result55->bind_param("sssiss", $name, $value, $expires, $new_visitor, $ip, $ua); $result55->execute(); $result55->close(); } $sql010->close();
额外优化建议
- 给
unique_vistor表的ip和ua字段加联合唯一索引,避免重复插入脏数据。 - 定期清理数据库中过期的访客记录,和Cookie有效期保持一致。
- 过滤掉非页面请求,例如静态资源、接口请求,仅统计页面访问的请求。
内容的提问来源于stack exchange,提问作者Rishabh Raj
相关产品推荐
相关产品推荐

