3x-ui 的 uTLS/fingerprint 填什么?TLS 指纹、JA3/JA4 实测讲清楚
不翻车编辑部 · 更新于

先说结论:3x-ui 里 REALITY 那一栏 uTLS 填 chrome,或者留空,别为了“更隐蔽”换成别的。2026 年 10 月 20 日我们在测试服务器上用 Xray v26.10.17 把下拉框里的选项挨个连了 3 次:留空、chrome、firefox、safari 能连上;ios、edge、qq、randomized、randomizednoalpn 被服务器当成普通访客,android 和 360 连握手都发不出去;random 重启 12 次,4 次连上、8 次连不上。下面讲这一栏到底是什么、墙怎么靠它认人、REALITY 为什么离不开它,原始数据在文末。

fingerprint 这一栏是什么
网址前面那把小锁就是 TLS,它负责给你和网站之间的数据加密。加密之前两边要先打招呼,商量用什么方法加密,这叫握手。
握手的第一句话由你的设备先说,正式名字叫 Client Hello(客户端问候)。可以把它想成进小区时在门卫那里填的访客登记表,上面写着三样东西:
- 你会哪几种加密方法(加密套件)
- 你带了哪些附加功能(扩展)
- 你准备用哪种钥匙(密钥交换)
这张表是明文的,加密还没开始,路上谁都看得见。每个程序填表的习惯都不一样:我们抓到正式版 Chrome 156 列了 15 种加密方法,Go 语言自带的 TLS 列 13 种,顺序、附加功能也不同。这就是 TLS 指纹。3x-ui 里叫 uTLS,客户端里叫 fingerprint、client-fingerprint,说的都是它。
JA3 和 JA4:把笔迹记成一串短码
门卫不会每张表都细看,他会把笔迹记成一串短码。业内常用两种:
- JA3:Salesforce 的工程师 2017 年公开。把版本、加密方法、扩展、曲线、点格式按原来的顺序串起来,算 MD5,得到 32 位编号。
- JA4:FoxIO 提出。先把清单排好序再算,结果分三段,比如
t13d1517h2_8daaf6152771_cb7bf5808d99:开头t13d1517h2是 TCP、TLS 1.3、带域名、15 种加密方法、17 个扩展、ALPN 是 h2;第二段是加密方法清单的短码;第三段是扩展和签名算法的短码。
区别在于 Chrome 现在每次都会打乱扩展的顺序。我们用正式版 Chrome 156 全新启动 3 次,3 个 JA3 全不一样,JA4 三次相同:


有个小坑:tshark 4.6.8 自带的 JA4 把签名算法里的 GREASE(浏览器故意塞的随机占位值)也算进了第三段,所以 tshark 给真 Chrome 算出的第三段每次不同。FoxIO 的说明写着任何位置的 GREASE 都要忽略,按这个重算,3 次都是 cb7bf5808d99。下文的 JA4 都按官方算法。
Go 默认指纹为什么扎眼
Xray、sing-box、mihomo 这三个常见内核都是 Go 写的。不做模仿时,它们用 Go 自带的 crypto/tls 填表:13 种加密方法,没有 GREASE。我们让三家都用 Go 自带的方式各连一次,JA4 中间那段都是 f57a46bbacb6,和 Chrome 的 8daaf6152771 对不上:

uTLS 的 README 原话:Go 的 Client Hello 指纹非常独特,在 Go 还不流行的手机上尤其扎眼;有人担心审查方按 Client Hello 直接封,误伤很小。

更早的证据是 NDSS 2019 的论文《The use of TLS in Censorship Circumvention》(Frolov、Wustrow):审查者已经用深度包检测按 TLS 指纹识别、封锁翻墙工具;作者分析了 9 个月里超过 118 亿次 TLS 连接,发现很多翻墙工具即使下功夫模仿浏览器,也容易被区分出来。uTLS 就是这篇论文的作者做的。

uTLS 怎么模仿 Chrome,像不像
uTLS 是 Go 自带 TLS 的一个分支,能逐格改写登记表,照着浏览器的样子抄。Xray 文档写着:fingerprint 默认值是 chrome,想恢复 Go 原生的 TLS,要专门设成 unsafe。源码里留空就直接返回 Chrome:


抄得像不像?Xray 填 chrome 发出的表和正式版 Chrome 156 放在一起:
- 加密方法 15 种,逐项相同(JA4 中间段都是
8daaf6152771) - 扩展:uTLS 16 个,真 Chrome 17 个,多的是
0xca34(BoringSSL 里叫 trust_anchors) - 签名算法:真 Chrome 多了
0x0904/0x0905/0x0906,是 ML-DSA 三种后量子签名 - 问候长度:uTLS 1747–1811 字节,真 Chrome 1913–1977 字节
原因是 Xray v26.10.17 用的 uTLS 里,HelloChrome_Auto 对应 Chrome 133,真 Chrome 已经往前走了二十多个版本。

sing-box 的文档态度更保守,把 uTLS 标成“不推荐”:浏览器用的是完全不同的 TLS 实现(Chrome 是 BoringSSL,Firefox 是 NSS),光复制握手格式,行为细节必然有差异;要抗指纹,它建议换用 NaiveProxy。两家看法不同,我们把数据摆出来,你自己判断。

REALITY 为什么必须配 fingerprint
REALITY 把服务器装成一个国外大网站,不是自己人来敲门,就原样转给那个真网站。我们用正式版 Chrome 打开测试服务器的 REALITY 端口(目标设的是 www.nvidia.com),看到的就是英伟达官网,证书也是英伟达的。

那它怎么认出自己人?暗号写在登记表上一个叫 Session ID(会话编号)的格子里,还加了密。Go 自带的 TLS 不让程序改这个格子,uTLS 可以,所以 REALITY 离不开它:

Xray 文档写得很清楚:REALITY 的 fingerprint 必填,不支持用 unsafe 禁用 utls,因为 REALITY 协议实现用这个库操作底层 TLS 参数。我们填 unsafe 试了,Xray 直接启动不了。留空没事,会当成 chrome。


外面的人看这张表,和 Xray 用 chrome 连普通 TLS 网站时完全一样,JA4 相同,看上去就是一个 Chrome 在访问英伟达:

每个选项怎么选:我们挨个连了一遍
客户端 Xray v26.10.17,服务端 Xray v26.10.17(VLESS + Vision + REALITY),每个选项连 3 次:
| fingerprint | 结果 | 原因 |
|---|---|---|
| 留空 / chrome | 3/3 能连 | 同一个指纹(Chrome 133) |
| firefox | 3/3 能连 | Firefox 148,带后量子钥匙 |
| safari | 3/3 能连 | Safari 26.3,带后量子钥匙 |
| ios | 0/3 | 对应 iOS 14,没有后量子钥匙,被当成普通访客 |
| edge | 0/3 | 对应 Edge 85,同上 |
| 0/3 | QQ 浏览器 11.1,同上 | |
| randomized / randomizednoalpn | 0/3 | 随机拼的指纹,没有后量子钥匙 |
| android | 0/3 | Android 11 OkHttp,不支持 TLS 1.3,握手没发出去 |
| 360 | 0/3 | 360 浏览器 7.5,同上 |
| random | 12 次里 4 次能连 | 启动时随机抽一个 |
| unsafe | 启动不了 | REALITY 不允许 |

连不上的几种,客户端日志是两种报错:被转给目标网站的写着“收到了真证书”,android 和 360 写着“这个指纹不支持 TLS 1.3”。

为什么老指纹连不上?Xray 作者 RPRX 2026 年 9 月 7 日在 Xray-core #6714 说,下个版本 REALITY 服务端要求 Client Hello 必须带 X25519MLKEM768(一种后量子密钥交换),否则回落。第二天 REALITY 库提交了这项改动,随 Xray v26.9.8 发布。uTLS 里 ios、edge、360、qq 这些预设抄的都是好几年前的浏览器,没有这一项。


random 是 Xray 启动时从 11 个“较新”指纹里随机抽一个,里面混着 Chrome 120、iOS 13/14、Edge 106 这些没有后量子钥匙的。我们重启 12 次,各连 1 次:

在哪填,客户端怎么对上
3x-ui:入站 → 安全 → Reality,第三行 uTLS(安全选 TLS 时也有同名一栏)。服务器自己并不用它,3x-ui 把它写进分享链接的 fp=,客户端导入时照着填;REALITY 链接还会自动加上 support-x25519mlkem768=true。

Clash 系(mihomo 内核,比如 Clash Verge):配置里叫 client-fingerprint。旁边的 fingerprint 是证书指纹,别填混。我们用 mihomo v1.19.32 连同一台服务器:
client-fingerprint: chrome加support-x25519mlkem768: true:能连chrome不开 mlkem:连不上(REALITY authentication failed)firefox开了 mlkem:也连不上

普通 TLS 节点也要注意:mihomo 里 client-fingerprint 不填,用的就是 Go 自带的那张表,我们抓到的 JA4 中间段和 sing-box 不开 uTLS 时一样。VLESS + TLS、Trojan 这类节点也建议写上 chrome。
sing-box 内核:utls 填 chrome 也连不上 Xray v26.9.8 之后的 REALITY(reality verification failed),它发出的表里少了后量子钥匙。来龙去脉见 Xray 和 sing-box 的 REALITY 之争。

局限:指纹只是第一关
- JA4 不是全部。mihomo 用 chrome 不开 mlkem 的那次,JA4 和能连上的那次一模一样,服务器照样认了出来,因为它看的是密钥交换那一格,JA4 不算这一项。
- 模仿只在第一张表。sing-box 文档说握手之后的行为和浏览器还有差别,这次没有测。
- 墙不会光看指纹,还会上门试探、看包的长短,见 Trojan 那篇的实测和协议从夯到拉。
- 这次是一台电脑、一台服务器、一个时间段的结果,真浏览器只对照了 Chrome 156。

怎么填,三条
- REALITY 节点:fingerprint 填 chrome,或者留空。
- Clash(mihomo):
client-fingerprint: chrome,连 REALITY 再加support-x25519mlkem768: true。 - 别选 random、ios、android、edge、360、qq、randomized。
REALITY 的目标网站怎么选,看 REALITY 的 SNI 填什么,也可以用 REALITY 目标网站检测查一下。
常见问题
fingerprint 换成 firefox 或 safari 会更隐蔽吗? 没有证据说明会。Xray 里 firefox、safari 能连 REALITY,但我们用 mihomo 试 firefox 就连不上,Xray 作者也说过 Mihomo 只能 Chrome。chrome 是 Xray 和 3x-ui 的默认值,换掉它看不到好处。
留空和填 chrome 有区别吗?
在 Xray 里没有。源码里留空直接返回 Chrome,我们抓到的 JA4 也一模一样。mihomo 里不一样:普通 TLS 节点不填 client-fingerprint,发出去的就是 Go 自带的表。
3x-ui 服务端改了 uTLS,服务器行为会变吗? 不会。服务端不用这个值,它只写进分享链接给客户端。改完要让客户端重新导入或更新订阅。
JA3 和 JA4 哪个更准? JA3 按原顺序算,Chrome 每次打乱扩展顺序,同一个浏览器 3 次 3 个 JA3;JA4 先排序,所以稳定。JA4 也不看密钥交换的具体组,我们抓到过 JA4 相同、一个能连一个连不上的例子。
为什么服务器升级后,原来能用的节点连不上了?
如果客户端选的是 ios、edge、qq 这类老指纹,或者是 sing-box 内核,Xray v26.9.8 起的 REALITY 会把它当普通访客。先把 fingerprint 改回 chrome;Clash 再打开 support-x25519mlkem768。
实测环境和资料来源
- 实测:不翻车测试服务器,Xray v26.10.17(服务端和客户端),sing-box v1.14.3,mihomo v1.19.32,正式版 Chrome 156.0.8078.47,tshark 4.6.8,2026-10-20。原始记录见仓库
docs/protocol-tests/2026-10-20-tls-fingerprint.md。 - 3x-ui v3.10.2 面板:测试服务器临时实例,截完即删。
- Xray 文档:TLS、REALITY
- Xray-core v26.10.17 源码 tls.go、reality.go、transport_security.go
- Xray-core #6714、REALITY 提交 8cdf7bf
- uTLS README、u_common.go
- Frolov、Wustrow,The use of TLS in Censorship Circumvention,NDSS 2019
- salesforce/ja3、FoxIO JA4 说明
- sing-box 文档:TLS
- mihomo 文档:TLS
- 3x-ui v3.10.2、inbound-link.ts
