应组合行为特征、服务端检测与动态响应来识别HeadlessChrome爬虫,而非仅依赖易伪造的User-Agent;PHP中需通过Referer、Accept、Cookie及JS验证等多维信号综合判断并分级处置。

识别 HeadlessChrome 的关键行为信号
HeadlessChrome 默认启动时会暴露多个非人行为痕迹,PHP 虽无法直接读取浏览器 DOM,但可通过请求上下文间接判断:
-
User-Agent 含可疑关键词但无配套行为:如 UA 中含
HeadlessChrome、Chrome/.*Headless,但同时$_SERVER['HTTP_REFERER']为空、$_SERVER['HTTP_X_REQUESTED_WITH']缺失、Accept-Language格式异常(如只有en-US无权重) -
无 Cookie 或会话异常:真实用户首次访问后通常有 Set-Cookie 响应,后续请求带有效 Cookie;而多数 HeadlessChrome 脚本不维护会话,
$_COOKIE为空或过期 -
JS 行为缺失的间接证据:例如页面中本应由 JS 注入的隐藏字段(如
_ts时间戳、_fp指纹 hash)未随 POST 提交,或提交时间间隔远小于 800ms(人类操作下限) -
Accept 头不完整:正常浏览器发送
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,而 HeadlessChrome 脚本常简化为*/*或缺失
PHP 中轻量级拦截逻辑(中间件推荐)
不要在每个控制器里写 if 判断。用 ThinkPHP 或原生 PHP 的前置钩子统一处理,例如:
在入口或中间件中加入:
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
$has_cookie = !empty($_COOKIE);
$is_headless_suspect = (
(stripos($ua, 'HeadlessChrome') !== false || stripos($ua, 'Chrome/') !== false && stripos($ua, 'Headless') !== false) &&
(empty($referer) || $referer === '-') &&
(empty($accept) || strpos($accept, 'text/html') === false) &&
!$has_cookie
);
if ($is_headless_suspect) {
// 记录日志,不立即封禁,先限流+打标
error_log("Suspicious HeadlessChrome-like request from {$_SERVER['REMOTE_ADDR']} at " . date('Y-m-d H:i:s'));
// 可选:写入 Redis 标记该 IP 为高风险,后续请求触发验证码
// redis()->setex('spider_ip:' . $_SERVER['REMOTE_ADDR'], 300, 1);
// 或返回轻量挑战(如跳转到带 JS 验证的中间页)
header('Location: /js-challenge?r=' . urlencode($_SERVER['REQUEST_URI']));
exit;
}
配合前端 JS 做二次验证(防绕过)
仅靠服务端头信息不可靠,必须引入客户端行为反馈:
立即学习“PHP免费学习笔记(深入)”;
- 在页面 HTML 中注入一段 JS,读取
navigator.webdriver、chrome对象、plugins.length等指标,生成一个签名(如sha256(navigator.platform + screen.width + hasWebGL)) - 将签名通过隐藏字段或 XHR 发送到 PHP 接口(如
/verify-browser),服务端比对是否符合真实浏览器常见值范围 - 若验证失败,后续接口返回 403 或要求重载带验证码的页面
注意:该 JS 不要放在外部文件,避免被爬虫预判过滤;可动态生成变量名或混淆执行顺序。
不建议的“硬拦”方式
以下做法实际效果差,还易伤及正常用户:
- 单纯匹配
HeadlessChrome就die('Forbidden')—— 攻击者改一行 UA 就绕过 - 用
get_headers()主动探测 UA —— 增加服务端开销,且无意义 - 依赖第三方库自动识别(如某些“浏览器指纹”PHP 包)—— 准确率低、更新滞后、误报高
- 把所有 Chrome 请求都限速 —— 影响大量真实移动端和桌面端用户
真正管用的,是把 HeadlessChrome 当作一个风险信号,而不是一个确定身份。结合 Referer、Cookie、Accept、JS 行为反馈,形成多维评分,再决定是放行、限速、弹验证码,还是临时隔离。



















