先看一条浏览器发出的 HTTP 请求:
GET /articles/42 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml
Accept-Language: zh-CN,zh;q=0.9
其中这一行就是通常所说的 UA:
User-Agent: Mozilla/5.0 ... Chrome/140.0.0.0 Safari/537.36
它由客户端放进请求头,用一段字符串描述“是谁在发请求”。浏览器会写入浏览器引擎、产品和操作系统等信息;命令行工具、移动 App、搜索爬虫和 AI Bot 也可以写入自己的名称。
但 UA 只是客户端的一段自我介绍,不是身份证。服务端可以读取它,客户端也可以随意修改它。理解这个边界,才能正确使用 UA。
UA 到底表示什么
User Agent 原本指代代表用户访问网络资源的软件。Chrome、Safari、微信 WebView、curl、搜索引擎爬虫,甚至后端服务调用方,都可以是 User Agent。
HTTP 中的 User-Agent 请求头则是这个客户端主动提交的标识。例如:
curl/8.7.1
PostmanRuntime/7.44.1
MyApp/3.2.0 (iOS 18.5; iPhone)
服务端在 Node.js 中可以直接读取:
export async function GET(request: Request) {
const userAgent = request.headers.get('user-agent') ?? '';
return Response.json({ userAgent });
}
在 Nginx 日志中,UA 通常来自 $http_user_agent:
log_format main '$remote_addr "$request" $status "$http_user_agent"';
access_log /var/log/nginx/access.log main;
因此,浏览器开发者工具里看到的是客户端准备发送的 UA,服务端访问日志里记录的则是服务端实际收到的 UA。请求经过 CDN、网关或反向代理时,还要确认中间层有没有删除、改写或另外保存原始值。
为什么一条浏览器 UA 看起来这么乱
下面是一条典型的 Chromium UA:
Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/140.0.0.0 Safari/537.36
它看起来同时包含 Mozilla、AppleWebKit、Chrome 和 Safari,并不代表用户同时安装了四个浏览器。这主要是历史兼容的结果。
早期网站会根据 UA 判断浏览器并决定是否返回某项功能。新浏览器为了获得原本只向主流浏览器开放的页面,会携带兼容标记。时间久了,UA 逐渐变成一条需要兼容旧规则的历史清单。
可以粗略理解为:
Mozilla/5.0:沿用至今的兼容标记,不代表当前浏览器就是 Firefox。Windows NT 10.0; Win64; x64:平台和架构信息。AppleWebKit/537.36:声明兼容 WebKit 风格的引擎标记。KHTML, like Gecko:历史兼容信息。Chrome/140.0.0.0:Chrome 产品及版本标记。Safari/537.36:用于兼容部分 Safari 检测逻辑的标记。
所以不能用 ua.includes('Safari') 就判定 Safari,也不能看到 Mozilla 就判定 Firefox。生产环境需要使用维护中的 UA 解析库,并准备好无法识别的新客户端。
UA 有哪些实际作用
1. 兼容性处理
某些浏览器、WebView 或旧设备确实存在特定兼容问题。服务端可以根据 UA 返回降级页面,前端也可以临时绕过已知 Bug。
但优先级应该是:
特性检测 > 渐进增强 > UA 判断
例如,要判断浏览器是否支持某个 API,应直接检查能力:
if ('IntersectionObserver' in window) {
// 使用对应能力
} else {
// 加载降级实现
}
UA 判断适合处理“某个已知版本实现有缺陷”之类的例外,不适合成为所有功能开关的基础。否则新浏览器、套壳浏览器和 UA 变化都可能导致误判。
2. 设备与页面适配
服务端有时需要在 HTML 生成前粗略判断移动端或桌面端,例如返回不同尺寸的图片、不同下载入口,或者为移动设备选择更轻的首屏内容。
const isMobile = /Android|iPhone|Mobile/i.test(userAgent);
这种简单规则可以用于非关键优化,但不应承担精确设备识别。平板、折叠屏、桌面模式、WebView 和修改过的 UA 都会破坏“手机或电脑”的二分法。页面布局通常更适合交给 CSS Media Query 和响应式设计。
3. 数据分析与故障定位
UA 是访问日志的重要维度,可以用来回答:
- 某次报错是否集中在 iOS WebView。
- 新版本发布后,旧浏览器失败率是否上升。
- 接口流量来自浏览器、App、脚本还是爬虫。
- 某个异常请求是否使用了批量脚本工具。
日志分析时最好保存原始 UA,同时再生成浏览器、操作系统、设备类型等结构化字段。只保存解析结果会丢失信息,也不便于以后用新版规则重新分析。
4. 爬虫识别和流量治理
搜索引擎通常会在 UA 中声明自己的爬虫名称。例如 Googlebot 的 UA 中会出现 Googlebot/2.1。服务端可以据此统计抓取量、检查 SEO 页面是否正常返回,或对爬虫设置独立的限流和缓存策略。
const crawlerPattern =
/Googlebot|bingbot|GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot/i;
const looksLikeCrawler = crawlerPattern.test(userAgent);
注意变量名是 looksLikeCrawler,而不是 isTrustedCrawler。这段代码只能说明 UA 看起来像爬虫,不能证明请求真的来自对应厂商。
5. API 客户端标识
自有 App、SDK 和后端服务也可以使用清晰的 UA,方便服务端排查版本问题:
User-Agent: Visionary-iOS/4.8.0 (iOS 18.5)
User-Agent: visionary-cli/1.6.2 node/22.17.0
这种标识非常适合可观测性,但仍然不能代替 Authorization、API Key、请求签名或 mTLS。UA 负责“方便识别”,认证信息负责“证明身份”。
爬虫会使用特殊请求头吗
合规爬虫通常会使用有辨识度的 UA,有时还会附带说明页面或联系方式:
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
它们并不一定拥有某个所有爬虫通用、且无法伪造的特殊请求头。大多数情况下,最明显的线索仍然是 User-Agent。
不同爬虫的行为大致分为三类:
| 类型 | 常见 UA 表现 | 目的 |
|---|---|---|
| 搜索引擎爬虫 | Googlebot、bingbot | 建立搜索索引 |
| 明确声明的内容爬虫 | 产品名、Bot 名称、说明 URL | 聚合、监控、训练或其他自动抓取 |
| 伪装型爬虫 | 模仿普通 Chrome、Safari | 绕过简单 UA 规则 |
使用 Playwright、Puppeteer 等真实浏览器内核的自动化程序,还可能自然带有接近普通浏览器的整套请求头。即使 UA 中没有 HeadlessChrome,它也不一定是真人访问。
因此,判断自动化流量时还会结合:
- 来源 IP、ASN,以及厂商公布的 IP 段。
- 正向和反向 DNS 验证。
- Cookie、JavaScript 执行和会话连续性。
- 请求频率、访问顺序、鼠标与页面行为。
Accept、Accept-Language、Referer、Sec-Fetch-*等请求头是否合理。- TLS 和 HTTP 协议层指纹。
这些信号也不是单独使用就绝对可靠。实际风控通常是多信号评分,而不是一条正则表达式。
AI 爬虫的 UA 有什么不同
AI 服务不一定只使用一个 Bot。以 OpenAI 官方文档列出的标识为例,不同 UA 对应不同目的:
| UA 标识 | 主要用途 |
|---|---|
GPTBot | 抓取可能用于改进模型的内容 |
OAI-SearchBot | 支持搜索结果中的发现与展示 |
ChatGPT-User | 响应用户操作访问页面 |
Anthropic 也使用 ClaudeBot、Claude-SearchBot 和 Claude-User 区分训练抓取、搜索抓取与用户触发访问。
这说明“AI 爬虫”并不是一种单一流量。网站可能愿意让内容进入 AI 搜索并获得引用,但不希望它被训练爬虫采集;也可能允许用户主动让 AI 读取某个公开页面。策略应该按 Bot 和用途配置,而不是用 /AI|GPT|Claude/i 一刀切。
例如,可以在 robots.txt 中分别表达策略:
User-agent: GPTBot
Disallow: /
User-agent: OAI-SearchBot
Allow: /
User-agent: ChatGPT-User
Allow: /
这里有两个重要边界:
robots.txt是面向合规爬虫的抓取约定,不是访问控制。私密内容必须依靠登录鉴权,不能只写Disallow。- Bot 名称和策略可能变化,上线前应查阅厂商最新官方文档,不要永久复制一份来源不明的 UA 清单。
还有一个容易误解的例子:Google-Extended 是 robots.txt 中用于控制相关生成式 AI 使用方式的产品令牌,并没有独立的同名 HTTP User-Agent。不能假设每一个 robots.txt 令牌都会原样出现在请求头里。
为什么不能只靠 UA 判断可信爬虫
任何客户端都可以发送下面的请求:
curl 'https://example.com/' \
-H 'User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
如果服务端看到 Googlebot 就绕过登录、风控或付费墙,攻击者也能获得同样权限。UA 不能用于:
- 鉴权或判断用户身份。
- 绕过权限校验、验证码和支付限制。
- 向所谓内部客户端返回密钥或未发布内容。
- 无条件把高消耗接口开放给“友好爬虫”。
对支持来源验证的厂商,应把 UA 与官方 IP 段或 DNS 验证结合。Google 官方也明确提醒 UA 可以伪造,并提供了爬虫验证方法。
如果只是为公开页面选择更合适的缓存、记录统计或返回可访问的静态 HTML,UA 误判的安全影响较低,可以把它当作第一层信号。如果判断结果会改变权限,UA 就远远不够。
在 Chrome 中临时修改 UA
如果想验证网站对不同 UA 的处理逻辑,可以直接在 Chrome DevTools 中临时修改当前标签页发送的 User-Agent。
- 打开 Chrome DevTools。
- 点击右上角的更多选项,依次选择 More tools -> Network conditions。
- 在 User agent 区域取消勾选 Use browser default。
- 选择预设值或输入自定义 UA,例如
OAI-SearchBot,然后刷新页面使其生效。
这种方式适合调试 UA 识别、页面渲染、日志记录和爬虫策略。它通常只影响当前 DevTools 对应的标签页,并需要在调试期间保持 DevTools 打开。
还要注意,把 UA 改成 OAI-SearchBot 只代表请求头“声称”自己是该 Bot,并不会让请求来自 OpenAI 的官方 IP,也不会自动获得真实爬虫的 DNS、网络特征或访问权限。这正好说明了为什么服务端不能仅凭 UA 验证爬虫身份。
UA 正在被减少:Client Hints 是什么
传统 UA 暴露的信息越细,越容易参与浏览器指纹识别。与此同时,复杂字符串也让兼容规则越来越脆弱。Chromium 因此逐步减少传统 UA 中可用的细粒度信息,并提供 User-Agent Client Hints。
Chromium 请求中可能出现:
Sec-CH-UA: "Not=A?Brand";v="99", "Chromium";v="140", "Google Chrome";v="140"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "macOS"
与一整条非结构化字符串相比,这些字段更容易解析。Sec-CH-UA 还可能故意包含一个看起来奇怪的品牌项,这是设计的一部分,业务代码不能默认列表中的每一项都是真实产品名。
服务端如果确实需要更高精度的信息,可以通过 Accept-CH 声明希望后续请求携带哪些提示:
Accept-CH: Sec-CH-UA-Platform-Version, Sec-CH-UA-Model
但 Client Hints 不是 UA 的全平台无缝替代品。它主要由 Chromium 系浏览器支持,部分高熵信息需要服务端请求,而且涉及缓存和隐私策略。实现时仍应遵循几个原则:
- 能做特性检测,就不识别浏览器品牌。
- 只请求业务真正需要的信息。
- 不把 Client Hints 当作认证信息。
- 使用相关字段生成不同响应时,正确设计
Vary和 CDN 缓存键。 - 对不支持 Client Hints 的客户端保留合理默认行为。
实际项目应该怎么处理 UA
可以按下面的顺序落地。
1. 先明确判断结果会影响什么
统计标签、调试信息和公开页面优化属于低风险用途;权限、价格、私密内容和风控放行属于高风险用途。后者不能只依赖 UA。
2. 保留原始值,再做结构化解析
日志中保存原始 UA,并额外记录解析后的浏览器、系统、设备类型和 Bot 类型。规则升级后,原始数据仍然可以重新计算。
3. 对未知客户端提供默认路径
不要建立“UA 不认识就拒绝”的白名单式兼容逻辑。新浏览器、隐私工具和内部客户端都可能没有进入现有规则库。
4. 把爬虫声明和身份验证分开
UA 命中 Bot 名称 -> 声明型爬虫
UA + 官方来源验证成功 -> 已验证爬虫
限流、缓存、日志和权限策略可以根据可信等级分别处理。
5. 定期维护 Bot 策略
搜索和 AI 厂商会新增、拆分或调整 Bot。维护一份包含用途、官方文档、验证方式、允许范围和更新时间的配置,比把长正则散落在业务代码中更可靠。
最后记住四句话
User-Agent 是客户端写入请求头的一段自我描述。
它适合兼容处理、数据分析、故障定位和爬虫初步分类。
正常浏览器、普通爬虫和 AI Bot 往往有不同 UA,但任意客户端都能伪造它。
因此,UA 可以回答“它声称自己是谁”,不能单独回答“它到底是谁”。