应将 UA 过滤统一收口至中间件(如 app/middleware/UserAgentFilter.php),白名单配置于 app/extra/spider.php,匹配用 stripos();须叠加 Cookie、Referer 校验,并在模型层查库+Redis 锁防刷,GPTBot 需 Nginx 层 403 阻断。

直接在 setInc() 前加 stripos($_SERVER['HTTP_USER_AGENT'], 'bot') 判断,会把 RSS 阅读器、微信内置浏览器、甚至部分 iOS WebView 全部误杀——这不是过滤爬虫,是自废武功。
用中间件统一拦截异常 UA,别在控制器里硬判断
控制器不是流量筛子。所有 UA 检查必须收口到中间件,否则后续加限流、打标、日志都得重复写。ThinkPHP 6+ 推荐路径:app/middleware/UserAgentFilter.php,handle() 中用 $request->header('user-agent') 取值($_SERVER 在 Swoole/FastCGI 下可能为空)。
- 白名单配置放在
app/extra/spider.php,返回数组如['Chrome', 'Firefox', 'Mobile Safari', 'WeChat'],避免每次请求查 DB - 匹配必须用
stripos(),不是==或正则全匹配——Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X)和Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X)都该放过 - 空 UA、纯数字(如
123456)、含python-requests/curl/httpie的字符串,记录日志后调用$this->halt()或走限流逻辑,不放行
光看 UA 根本拦不住真实爬虫,必须叠加 Cookie 和 Referer 校验
User-Agent 完全可伪造,curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" 一行就能绕过全部白名单。真正有效的组合判断是:
- 检查
$request->cookie('thinkphp_token')是否存在且未过期——真用户有会话,多数爬虫连 Cookie jar 都不维护 - 检查
$request->header('referer')是否非空且域名与本站一致(可用parse_url()提取 host 对比) - 对 GET /article/123 这类详情页,若 UA 合法但
X-Requested-With为空、Referer为空、无 Cookie,直接跳过阅读计数
阅读计数逻辑必须和路由参数解耦,别用 param() 读 ID
param('id') 默认不走全局 default_filter,也不受中间件 mergeParam() 影响,容易被构造恶意路由参数刷量。正确做法是:
立即学习“PHP免费学习笔记(深入)”;
- 用
input('get.id', 0, 'intval')显式从 GET 取值并过滤,确保为正整数 - 在模型层做双重校验:
Db::name('article')->where('id', $id)->value('id')存在才执行setInc() - 计数更新前加 Redis 锁:
Cache::store('redis')->lock("view_{$id}", 2)->get(function () use ($id) { ... }),防并发重复计数
OpenAI 的 GPTBot 必须单独封禁,robots.txt 不起作用
robots.txt 对 GPTBot 是摆设,它明确声明“尊重但不强制遵守”。千万级请求下,靠 PHP 层拦截已来不及。必须在 Web 服务器层前置阻断:
- Nginx 配置中加:
if ($http_user_agent ~* "GPTBot|Google-Extended") { return 403; } - 或更彻底:用
include引入动态 IP 黑名单文件(路径如/www/wwwroot/mysite.com/ip_blacklist.conf),脚本自动将高频 GPTBot 请求 IP 写入并重载 Nginx - 注意:Nginx 的
$http_user_agent区分大小写,~*才能匹配GPTBot和gptbot
最易被忽略的点:阅读计数更新操作本身不能暴露在公开接口中,必须和文章展示逻辑分离;否则攻击者绕过 UA 判断,直接 POST /api/increase-view?id=123 就能刷爆。计数动作应只响应合法页面加载,并绑定会话与来源上下文。



















