从零部署 Xray REALITY:Clash DNS 防泄露、Google GeoIP 与 Gemini/HomeIP 排障实录
这篇文章记录一次完整的自建代理部署与排障过程:在 Ubuntu VPS 上通过官方脚本安装 Xray-core,配置 VLESS + REALITY + XTLS Vision,在 macOS 上使用 Clash Verge Rev 做国内外分流,并处理 DNS 泄露与 Google GeoIP 误判。最后,由于 Google 将整个 VPS 网段错误识别为中国,我将 Gemini 单独切换到 HomeIP 出口。
本文重点不只是“复制命令”,而是每一步如何验证,以及遇到问题后怎样区分服务端、客户端、DNS 和目标网站自身的地区数据库。
本文仅用于合法的自建网络、隐私保护和运维研究。请遵守所在地法律法规、云服务商条款和目标服务条款。文中所有地址与凭据均为占位符,请勿把真实 UUID、私钥、Short ID、订阅 URL 或账户凭据提交到公开仓库。
一、最终架构
最终流量分为三类:
┌─ 中国域名 / 中国 IP
│ DIRECT + 国内 DoH
Mac / iPhone ─ Clash ────┤
├─ 普通国外流量
│ RackNerd VPS → Xray REALITY
│
└─ Gemini / Google AI
HomeIP 专用出口
选择这套结构的原因:
- 国内流量直连,避免不必要的跨境绕路;
- 普通国外流量使用自己的 VPS,控制力更强;
- Gemini 对 IP 地区和信誉的判断与普通 GeoIP 数据库不同,因此单独使用 HomeIP;
- DNS 根据域名类别选择解析器,避免“网页走代理、DNS 仍在本地”的泄露。
二、环境与目标
这次使用的环境如下:
| 组件 | 环境 |
|---|---|
| VPS | Ubuntu 24.04 LTS,约 1 GB RAM |
| 服务端 | Xray-core 26.x |
| 安装器 | XTLS 官方 Xray-install |
| 协议 | VLESS + REALITY + XTLS Vision |
| 传输 | TCP/RAW 443 |
| macOS 客户端 | Clash Verge Rev 2.5.x / Mihomo 1.19.x |
| iOS 客户端 | Shadowrocket |
| 拥塞控制 | BBR + fq |
对于个人使用,1 GB RAM 已经足够。实际体验更依赖线路、晚高峰拥塞、IP 信誉和服务商端口策略,而不是磁盘或 CPU。
三、先配置 SSH 密钥登录
3.1 生成密钥
macOS 上优先使用 Ed25519:
ssh-keygen -t ed25519 -a 100
默认会生成:
~/.ssh/id_ed25519
~/.ssh/id_ed25519.pub
私钥 id_ed25519 只能留在本机,服务器只保存 .pub 公钥。
3.2 安装公钥
把 <VPS_IP> 换成服务器地址:
cat ~/.ssh/id_ed25519.pub | ssh root@<VPS_IP> \
'umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys; chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys'
这一步需要最后输入一次服务器密码。随后测试:
ssh -o BatchMode=yes -o IdentitiesOnly=yes \
-i ~/.ssh/id_ed25519 root@<VPS_IP> 'id -un && hostname'
确认免密登录成功后,再考虑关闭 SSH 密码认证。不要在密钥尚未验证时关闭密码登录,否则容易把自己锁在服务器外。
四、使用官方脚本安装 Xray-core
项目地址:
我没有直接执行未知的一键脚本,而是先下载并检查语法:
curl -fsSL -o /tmp/xray-install.sh \
https://raw.githubusercontent.com/XTLS/Xray-install/main/install-release.sh
bash -n /tmp/xray-install.sh
sha256sum /tmp/xray-install.sh
确认来源和脚本无误后执行:
bash /tmp/xray-install.sh install
官方脚本会安装:
/usr/local/bin/xray
/usr/local/etc/xray/config.json
/usr/local/share/xray/geoip.dat
/usr/local/share/xray/geosite.dat
/etc/systemd/system/xray.service
检查版本和服务:
xray version
systemctl is-enabled xray
systemctl is-active xray
4.1 使用仓库脚本快速部署
为了以后更换 VPS 或在备用节点上重复部署,我把上述过程整理成了脚本:
建议先下载并审阅,不要直接 curl | bash:
curl -fsSL -o deploy-xray-reality.sh \
https://raw.githubusercontent.com/alloevil/alloevil.github.io/master/assets/scripts/deploy-xray-reality.sh
less deploy-xray-reality.sh
bash -n deploy-xray-reality.sh
在全新的 Ubuntu VPS 上执行:
sudo bash deploy-xray-reality.sh --address <VPS_IP>
常用选项:
--port <PORT> 修改 Xray 监听端口,默认 443
--target <DOMAIN> 修改 REALITY 目标和 SNI,默认 www.apple.com
--no-bbr 跳过 BBR + fq
--force 备份并替换已有 Xray 配置
脚本完成后会把客户端参数保存到:
/root/xray-client-info.txt
该文件权限为 600,包含完整客户端链接,不应上传到 GitHub 或发送给不可信的人。
如果服务器已有配置,脚本默认停止。只有明确需要轮换 UUID、REALITY 密钥和 Short ID 时才使用 --force;执行后旧客户端会立即失效,需要全部重新导入。
脚本不会自动修改 SSH、UFW、云安全组或服务商后台规则。TCP 443 仍需在云防火墙中单独放行。
五、配置 VLESS + REALITY + XTLS Vision
5.1 生成参数
生成 UUID:
xray uuid
生成 REALITY X25519 密钥:
xray x25519
新版本可能输出:
PrivateKey: <REALITY_PRIVATE_KEY>
Password (PublicKey): <REALITY_PUBLIC_KEY>
Hash32: ...
服务端使用私钥,客户端只使用公钥。不要把私钥发给客户端或写进公开文档。
生成 Short ID:
openssl rand -hex 8
5.2 服务端配置
将 /usr/local/etc/xray/config.json 配置为:
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "vless-reality-in",
"listen": "0.0.0.0",
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "<UUID>",
"flow": "xtls-rprx-vision",
"email": "personal"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "raw",
"security": "reality",
"realitySettings": {
"show": false,
"target": "www.apple.com:443",
"xver": 0,
"serverNames": [
"www.apple.com"
],
"privateKey": "<REALITY_PRIVATE_KEY>",
"shortIds": [
"<SHORT_ID>"
]
}
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "blocked",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "blocked"
},
{
"type": "field",
"protocol": [
"bittorrent"
],
"outboundTag": "blocked"
}
]
}
}
这里阻止了代理访问私有网段,并禁用 BitTorrent,降低误用、SSRF 和投诉风险。
5.3 先校验,再重启
xray run -test -config /usr/local/etc/xray/config.json
systemctl restart xray
systemctl status xray --no-pager
ss -ltnp 'sport = :443'
正确结果应包含:
Configuration OK
active (running)
LISTEN *:443
还可以从客户端机器测试 REALITY 的未认证回落行为:
openssl s_client \
-connect <VPS_IP>:443 \
-servername www.apple.com \
-verify_return_error </dev/null
如果返回 Apple 的有效 TLS 1.3 证书,说明 443、公网安全组和 REALITY 回落路径都正常。
六、启用 BBR + fq
先确认内核支持:
modinfo tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control
加载模块:
modprobe tcp_bbr
持久化模块加载:
printf '%s\n' 'tcp_bbr' > /etc/modules-load.d/xray-bbr.conf
创建 /etc/sysctl.d/99-xray-network.conf:
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
即时应用:
sysctl -w net.core.default_qdisc=fq
sysctl -w net.ipv4.tcp_congestion_control=bbr
验证:
sysctl net.core.default_qdisc
sysctl net.ipv4.tcp_congestion_control
ss -tin
旧 TCP 连接仍可能显示 cubic,新连接才会显示 bbr。为确保 Xray 的监听套接字在新默认值下创建,我最后重启了一次 Xray。
BBR 可以改善高延迟链路的拥塞恢复,但无法解决 VPS 超售、线路绕路或晚高峰带宽不足。
七、Shadowrocket 与 Clash 客户端参数
7.1 Shadowrocket
核心参数如下:
类型:VLESS
地址:<VPS_IP>
端口:443
UUID:<UUID>
传输:TCP
流控:xtls-rprx-vision
TLS/安全:REALITY
SNI:www.apple.com
指纹:chrome
Public Key:<REALITY_PUBLIC_KEY>
Short ID:<SHORT_ID>
允许不安全:关闭
Shadowrocket 新版本不一定显示一个单独名为“REALITY”的节点类型。只要节点类型为 VLESS,并出现 Public Key、Short ID、SNI 和指纹字段,就已经识别了 REALITY。
7.2 Clash/Mihomo 节点
proxies:
- name: RackNerd-Reality
type: vless
server: <VPS_IP>
port: 443
uuid: <UUID>
network: tcp
tls: true
udp: true
flow: xtls-rprx-vision
servername: www.apple.com
client-fingerprint: chrome
reality-opts:
public-key: <REALITY_PUBLIC_KEY>
short-id: <SHORT_ID>
客户端参数必须与服务端完全对应。SNI、公钥或 Short ID 任一不一致,都会导致握手失败。
八、Clash Verge Rev 的国内外 DNS 分流
8.1 为什么“同时写国内外 DNS”仍会泄露
最初的配置同时查询国内 DNS 和 Google/Cloudflare DoH。网页出口虽然是美国,但 DNS 检测仍能看到中国电信和 CNISP 的解析器。
原因是 Mihomo 会并发查询多个默认 nameserver,谁先返回就使用谁;即使最终选用了海外结果,中国 DNS 查询也已经发生,权威 DNS 检测站仍能看到它。
正确做法不是“多写几个 DNS”,而是通过 nameserver-policy 决定不同域名只能查询哪一组解析器。
8.2 最终 DNS 配置
下面是一份脱敏后的通用版本:
dns:
enable: true
listen: :53
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
ipv6: false
prefer-h3: false
respect-rules: true
use-hosts: true
use-system-hosts: false
default-nameserver:
- 223.5.5.5
- 119.29.29.29
# 直连的中国域名使用国内加密 DNS
direct-nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
direct-nameserver-follow-policy: true
# 未分类域名默认走海外 DoH,避免随机测试域名触发国内 DNS
nameserver:
- https://8.8.8.8/dns-query
- https://1.1.1.1/dns-query
fallback:
- https://8.8.8.8/dns-query
- https://1.1.1.1/dns-query
# 解析代理节点域名,避免“必须先连代理才能解析代理”的循环
proxy-server-nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
nameserver-policy:
geosite:cn:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
geosite:geolocation-!cn:
- https://8.8.8.8/dns-query
- https://1.1.1.1/dns-query
fake-ip-filter-mode: blacklist
fake-ip-filter:
- '*.lan'
- '*.local'
- '*.arpa'
- geosite:private
- geosite:cn
这套策略的取舍是:
- 中国域名返回真实 IP,兼容国内 App、支付和局域网;
- 国外域名使用 Fake-IP,由 Clash 完成映射;
- 未分类域名默认使用海外 DoH,优先保证隐私;
- 极少数没有被 GeoSite 收录的国内域名可能通过海外 DNS 解析,速度会稍慢,但流量仍可按规则直连。
8.3 TUN 必须实际启用
配置文件写着 tun.enable: true 不代表运行时一定生效。应当通过 Mihomo API 或 Clash Verge Rev 界面检查运行状态。
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
macOS 如果手动指定了一个落在 TUN 排除网段中的企业 DNS,该 DNS 会绕过 any:53。我的解决方式是给 Wi-Fi 配置一个能被 TUN 捕获、Clash 关闭时也能正常使用的公共 DNS:
networksetup -setdnsservers Wi-Fi 223.5.5.5 119.29.29.29
dscacheutil -flushcache
8.4 一处语法错误如何让整个配置失效
排障时还遇到一个容易忽略的问题:nameserver-policy 中原本存在类似下面的键:
+example.cn:
- 10.0.0.53
Mihomo 要求 + 通配符占据完整标签。应改成精确域名:
example.cn:
- 10.0.0.53
或者合法的子域通配:
+.example.cn:
- 10.0.0.53
这一个错误会导致最终配置验证失败。Clash Verge Rev 会退回最小配置启动,表现为:界面还开着,但 TUN 和 DNS section 实际关闭,流量可能直接连接。
因此每次修改后都要检查应用日志和运行时状态,而不是只看 YAML 是否能被普通解析器读取。
九、验证 DNS 是否真的修好
9.1 本地解析行为
dig +short www.google.com A
dig +short www.baidu.com A
预期结果:
Google → 198.18.0.0/16 内的 Fake-IP
百度 → 真实中国 IP
这说明国外域名被 Clash 接管,国内域名仍保持真实解析。
9.2 网页深度检测
我使用了:
https://ip.net.coffee/dns/
第一次检测同时出现 Google/Cloudflare 和中国电信/CNISP,说明虽然海外 DNS 已启用,但国内 nameserver 仍被并发查询。
将默认 nameserver 改为海外 DoH,仅让 geosite:cn 使用国内 DoH 后,深度检测结果变成:
出口 IP:美国 VPS
DNS 解析器:Google / Cloudflare 美国节点
中国 DNS:0
结论:未检测到 DNS 泄露
注意,公共 DNS 的出口 IP 不需要等于 VPS IP。判断重点是它是否仍暴露本地运营商和真实地区。
十、Google GeoIP:第三方显示美国,Google 却显示武汉
DNS 修复后,Google 搜索页底部仍然显示:
Wuhan, Hubei, China — From your IP address
而常见 IP 数据库将 VPS 判断为美国 Dallas(换 IP 后 ipinfo 显示为 Los Angeles)。登录 Gemini 后则提示:
Gemini isn’t currently supported in your country.
为了排除分流错误,我检查了 Mihomo 的活动连接。所有 Google 请求都经过:
gemini.google.com → RackNerd-Reality → Proxy → Manual
accounts.google.com → RackNerd-Reality → Proxy → Manual
googleapis.com → RackNerd-Reality → Proxy → Manual
gstatic.com → RackNerd-Reality → Proxy → Manual
所以这不是 DNS 泄露或 Google 域名直连,而是 Google 自己的 GeoIP 数据与第三方数据库不一致。
10.1 为什么换相邻 IP 没用
我把 VPS IP 换成了同一网段中的另一个地址。第三方数据库仍显示美国,但 Google 继续判断为武汉。
原因很可能是 Google 按整个 /24 网段维护位置。只改最后一个数字,仍然落在同一个地址块中。
如果要求云厂商换 IP,应明确说明:
Please assign an IP from a completely different /24 subnet.
The current subnet is incorrectly geolocated by Google as China.
换到不同 /24,最好再换 ASN 或机房,才有机会改变 Google 的判断。
10.2 Google 搜索位置不是通用 GeoIP 真值
Google 没有公开一个可输入任意 IP 的 GeoIP 查询页面。最接近 Google 自身判断的方法,是在无痕窗口拒绝定位权限后打开:
https://www.google.com/search?q=where+am+i&hl=en&pws=0
查看页面底部的 From your IP address。但最终仍要以登录后的 Gemini 能否对话为准。
十一、最终通过 HomeIP 解决 Gemini
由于自建 VPS 的整个网段都被 Google 误判,我最终没有继续修改 DNS、REALITY 或浏览器指纹,而是使用 HomeIP 作为 Gemini 的专用出口。
核心原则是:同一 Google 会话相关的域名尽量使用同一个出口,避免登录请求、Gemini 页面和 API 来自不同国家。
在 Clash 中可以建立一个独立策略组:
proxy-groups:
- name: Gemini
type: select
proxies:
- <HOMEIP_NODE>
- RackNerd-Reality
然后把 Gemini 相关域名放在中国直连和默认规则之前:
rules:
- DOMAIN,gemini.google.com,Gemini
- DOMAIN,aistudio.google.com,Gemini
- DOMAIN,ai.google.dev,Gemini
- DOMAIN,accounts.google.com,Gemini
- DOMAIN,ogs.google.com,Gemini
- DOMAIN-SUFFIX,gemini.gstatic.com,Gemini
- DOMAIN-SUFFIX,generativelanguage.googleapis.com,Gemini
- DOMAIN-SUFFIX,googleapis.com,Gemini
- DOMAIN-SUFFIX,gstatic.com,Gemini
- DOMAIN-SUFFIX,googleusercontent.com,Gemini
- GEOSITE,private,DIRECT
- GEOSITE,cn,DIRECT
- GEOIP,private,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,Manual
实际依赖域名会随 Google 产品更新。如果仍出现地区不一致,应查看 Mihomo 活动连接,确认所有 Gemini 会话域名都经过同一策略组。
HomeIP 解决的是“目标服务对出口 IP 的地区/信誉要求”,不是 DNS 泄露。两者不能混为一谈:
- DNS 泄露:本地运营商能看到查询,靠 TUN、DoH 和 nameserver-policy 修复;
- GeoIP 误判:目标服务认为出口 IP 属于错误国家,通常只能更换出口;
- 账户地区:与 Cookie、付款资料和 Google 账户历史有关,需要单独验证。
使用第三方出口时也要理解隐私边界:HTTPS 内容仍然加密,但提供商可以看到连接目标和流量元数据。因此我只让 Gemini/Google AI 使用 HomeIP,其余流量继续走自建 VPS。
十二、服务端链式代理:把住宅 IP 挂在 VPS 后面
12.1 为什么在服务端做
客户端方案(在 Clash 里写 dialer-proxy 链式节点)也能用,但每台设备都要单独配一遍,iPhone 上的小火箭、家里的路由器还得各写一套。服务端方案只在 VPS 上配一次,所有连到这台 VPS 的客户端(Clash、小火箭、路由器)都自动生效,客户端订阅一行都不用改。
| 客户端链式(Clash) | 服务端链式(VPS) | |
|---|---|---|
| 配置次数 | 每台设备一次 | 一次 |
| 覆盖范围 | 只有配了的设备 | 全部设备 |
| 住宅 IP 凭据 | 分散在每个客户端 | 只存在 VPS 上 |
| 灵活度 | 每台设备可以不同 | 所有设备规则统一 |
流量路径:客户端 → VPS(Reality / Hysteria2)→ 住宅 IP(SOCKS5)→ Google / AI 网站,其他流量照旧 客户端 → VPS → 直出。
12.2 先确认住宅代理的协议
在 VPS 上分别用 SOCKS5 和 HTTP 测一下,哪个有返回就用哪个:
curl -s -m 15 -x socks5h://<RESI_USER>:<RESI_PASS>@<RESI_HOST>:<RESI_PORT> https://ipinfo.io/json
curl -s -m 15 -x http://<RESI_USER>:<RESI_PASS>@<RESI_HOST>:<RESI_PORT> https://ipinfo.io/json
我这家只有 SOCKS5 有返回,ip 是住宅 IP,city 是 Mesa, Arizona。
12.3 xray:加一个 resi outbound
/usr/local/etc/xray/config.json 的 outbounds 里加一个 tag 为 resi 的出站,direct 仍然放在第一个,作为默认出口:
"outbounds": [
{ "tag": "direct", "protocol": "freedom" },
{ "tag": "blocked", "protocol": "blackhole" },
{
"tag": "resi",
"protocol": "socks",
"settings": {
"servers": [{
"address": "<RESI_HOST>",
"port": <RESI_PORT>,
"users": [{ "user": "<RESI_USER>", "pass": "<RESI_PASS>" }]
}]
}
},
{ "tag": "block", "protocol": "blackhole" }
]
routing.rules 里按域名把 Google 和 AI 网站分到 resi(YouTube 规则见下一节,必须放在它前面):
{
"type": "field",
"domain": [
"domain:google.com", "domain:googleapis.com", "domain:gstatic.com",
"domain:googleusercontent.com", "domain:gmail.com", "domain:google.dev",
"domain:openai.com", "domain:chatgpt.com", "domain:oaistatic.com", "domain:oaiusercontent.com",
"domain:anthropic.com", "domain:claude.ai", "domain:claude.com",
"domain:ipinfo.io"
],
"outboundTag": "resi"
}
ipinfo.io 是故意加进去的,方便验证。改完先测再重启:
xray run -test -c /usr/local/etc/xray/config.json
systemctl restart xray && systemctl is-active xray
12.4 Hysteria2:outbounds + ACL
/etc/hysteria/config.yaml 末尾追加:
outbounds:
- name: direct
type: direct
- name: resi
type: socks5
socks5:
addr: <RESI_HOST>:<RESI_PORT>
username: <RESI_USER>
password: <RESI_PASS>
acl:
inline:
- direct(suffix:youtube.com)
# ……其他 YouTube 域名,见下一节
- resi(suffix:google.com)
- resi(suffix:googleapis.com)
# ……与 xray 相同的域名列表
- resi(suffix:ipinfo.io)
- direct(all)
ACL 从上往下匹配,第一条命中就结束,所以最后一定要有 direct(all) 兜底。
systemctl restart hysteria-server && systemctl is-active hysteria-server
12.5 验证
在客户端(或者本地起一个 xray 客户端开 socks 端口)分别访问:
curl -s -x socks5h://127.0.0.1:<LOCAL_PORT> https://ipinfo.io/json # 应显示住宅 IP,city 为住宅 IP 所在地
curl -s -x socks5h://127.0.0.1:<LOCAL_PORT> https://api.ipify.org # 应显示 <VPS_IP>
curl -s -o /dev/null -w '%{http_code}\n' -x socks5h://127.0.0.1:<LOCAL_PORT> https://www.google.com # 200
ipinfo 在分流列表里,ipify 不在,两个结果不一样就说明分流生效了:列表里的网站走住宅 IP,其他走 VPS。
注意要用
socks5h://(域名在代理端解析),用socks5://时 curl 会先在本地解析成 IP 再发过去,服务端拿不到域名,域名规则就不生效(开了 sniffing 后能补救,见第十四节)。
另外,用 curl 访问 claude.ai、chatgpt.com 返回 403 是正常的,这是 Cloudflare 拦截脚本请求,浏览器里打开没问题。
十三、排除 YouTube:视频不要走住宅 IP
13.1 为什么
住宅 IP 带宽有限、有的还按流量计费,YouTube 走它既慢又浪费。而 YouTube 属于 Google,如果规则写成”Google 全走住宅”,视频流量会一起被带进去。
13.2 为什么不用 geosite:google
geosite:google 这个分类里包含 youtube.com、googlevideo.com、ytimg.com 等域名,用它等于把 YouTube 也送进住宅 IP。所以这里改成手写域名列表,只列需要”看起来像美国家庭用户”的域名,YouTube 再单独写一条 direct 规则,放在 Google 规则前面。xray 和 Hysteria2 都是按顺序匹配、命中即停:
xray:
{ "type": "field", "ip": ["geoip:private"], "outboundTag": "blocked" },
{
"type": "field",
"domain": [
"domain:youtube.com", "domain:googlevideo.com", "domain:ytimg.com",
"domain:youtu.be", "domain:ggpht.com", "domain:youtube-nocookie.com"
],
"outboundTag": "direct"
},
{ "...": "然后才是 resi 规则" }
Hysteria2:
acl:
inline:
- direct(suffix:youtube.com)
- direct(suffix:googlevideo.com)
- direct(suffix:ytimg.com)
- direct(suffix:youtu.be)
- direct(suffix:ggpht.com)
- direct(suffix:youtube-nocookie.com)
- resi(suffix:google.com)
# ...
13.3 带宽实测
在 VPS 上从 Cloudflare 测速节点下载 20MB:
# 经住宅 IP
curl -s -o /dev/null -x socks5h://<RESI_USER>:<RESI_PASS>@<RESI_HOST>:<RESI_PORT> \
-w '%{speed_download} B/s\n' 'https://speed.cloudflare.com/__down?bytes=20000000'
# VPS 直出
curl -s -o /dev/null -w '%{speed_download} B/s\n' 'https://speed.cloudflare.com/__down?bytes=20000000'
| 出口 | 下载速度 |
|---|---|
| 住宅 IP | 约 7.4 MB/s(≈59 Mbps) |
| VPS 直出 | 约 65 MB/s(≈520 Mbps) |
住宅 IP 打开 Google 首页约 0.15 s,用来搜索和聊 AI 绰绰有余,但跑视频差了将近 9 倍,所以 YouTube 一定要排除。
13.4 验证
curl -s -o /dev/null -w '%{http_code}\n' -x socks5h://127.0.0.1:<LOCAL_PORT> https://www.youtube.com # 200
也可以在 YouTube 播放器上右键”详细统计信息”,看连接速度是不是跟以前一样。
十四、QUIC 导致 Google 定位仍然不对
14.1 现象
链式代理配好后,ipinfo.io 显示的是住宅 IP,一切正常,但 Chrome 里 Google 搜 “my location” 还是显示广东。在 chrome://flags 里把 Experimental QUIC protocol 改成 Disabled 并重启 Chrome 后,定位马上变成美国。
14.2 原因
Chrome 访问 Google 默认优先走 QUIC(HTTP/3,UDP 443)。问题出在两处:
- 分流规则拿不到域名。 Clash TUN 模式 + fake-ip 下,UDP 包到达 VPS 时可能只带 IP、不带域名;服务端没开 sniffing,就不知道这个包是发给 google.com 的,域名规则匹配不上,直接走了
direct,出口是 VPS IP,而 Google 的 IP 库把 VPS IP 标成了国内。 - 住宅 SOCKS5 多半不支持 UDP。 就算匹配上了,QUIC 也没法稳定地经住宅 IP 出去。
所以 TCP 的 HTTPS 请求走了住宅 IP,QUIC 请求却从 VPS 出去,Google 两边都看到了,就按 VPS IP 定位。ipinfo 不用 QUIC,所以看起来一切正常。
14.3 修复思路
不让 Google/AI 的 QUIC 出去,浏览器发现 UDP 443 不通会自动降级到 TCP(HTTPS),而 TCP 已经走住宅 IP。这样客户端什么都不用改,Chrome 的 QUIC 也可以重新打开。
14.4 xray
- 给 VLESS inbound 开 sniffing,从 TLS SNI / HTTP Host / QUIC 握手里还原域名:
"inbounds": [{
"port": 443,
"protocol": "vless",
"settings": { "...": "..." },
"streamSettings": { "...": "..." },
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"],
"routeOnly": true
}
}]
routeOnly: true 只用嗅探出的域名做路由匹配,实际连接目标不变,不会因为改写目标地址带来副作用。
- 加 blackhole 出站,并在 resi 规则之前插一条”UDP 443 + 同一份域名 → block”:
{ "tag": "block", "protocol": "blackhole" }
"rules": [
{ "type": "field", "ip": ["geoip:private"], "outboundTag": "blocked" },
{ "type": "field", "domain": ["domain:youtube.com", "domain:googlevideo.com", "..."], "outboundTag": "direct" },
{ "type": "field", "network": "udp", "port": "443",
"domain": ["domain:google.com", "domain:googleapis.com", "...与 resi 相同的列表"],
"outboundTag": "block" },
{ "type": "field", "domain": ["domain:google.com", "..."], "outboundTag": "resi" },
{ "type": "field", "protocol": ["bittorrent"], "outboundTag": "blocked" }
]
最终顺序:私有 IP 屏蔽 → YouTube 直连(TCP/UDP 都直连,YouTube 的 QUIC 不受影响)→ Google/AI 的 UDP 443 屏蔽 → Google/AI 走住宅 → BT 屏蔽 → 其余默认直连。
这里没有用 geoip:google 按 IP 屏蔽 UDP,因为 Google 的 IP 段里也有 YouTube 视频服务器,会误伤 YouTube 的 QUIC;按域名 + sniffing 已经够用。
14.5 Hysteria2
ACL 支持在规则后面写协议和端口,在 resi 行前面插入 reject:
acl:
inline:
- direct(suffix:youtube.com)
# ……YouTube 其他域名
- reject(suffix:google.com, udp/443)
- reject(suffix:googleapis.com, udp/443)
# ……与 resi 相同的列表
- resi(suffix:google.com)
- resi(suffix:googleapis.com)
# ……
- direct(all)
xray run -test -c /usr/local/etc/xray/config.json
systemctl restart xray hysteria-server
systemctl is-active xray hysteria-server
14.6 验证
- 先重跑上面的命令行测试:ipinfo 显示住宅 IP,ipify 显示
<VPS_IP>,google.com 返回 200,youtube.com 返回 200。 - 在
chrome://flags里把 QUIC 改回 Default,重启 Chrome。 - 打开 Google 搜 “my location”,页面底部应显示美国(住宅 IP 所在地),不再是广东或武汉。
- 想确认 Google 走的是 TCP:打开开发者工具 → Network,右键表头勾选 Protocol,google.com 的请求应显示
h2而不是h3;YouTube 视频请求仍可以是h3。
十五、完整排障顺序
以后遇到“节点测试正常,但网页打不开”时,可以按下面顺序检查。
15.1 服务端
systemctl is-active xray
xray run -test -config /usr/local/etc/xray/config.json
ss -ltnp 'sport = :443'
journalctl -u xray -p err -n 50 --no-pager
15.2 客户端节点
检查:
- IP 和端口;
- UUID;
- SNI;
- REALITY Public Key;
- Short ID;
xtls-rprx-vision;- TLS 指纹。
15.3 Clash 运行时
不要只看 UI 开关,还要确认:
mode=rule
tun.enable=true
dns-hijack=any:53
目标域名的连接链包含预期代理组
15.4 DNS
dig +short www.google.com A
dig +short www.baidu.com A
随后做权威 DNS 深度测试,确认没有真实运营商解析器。
15.5 目标服务
如果网络、TLS、DNS 和路由均正常,但目标服务仍提示地区不支持,就检查:
- 无痕窗口是否能用;
- 登录前后是否不同;
- Google 搜索页底部如何判断位置;
- 同一服务的所有域名是否使用同一出口;
- 是否需要不同网段、不同 ASN 或住宅/家庭网络出口;
- 在
chrome://flags关闭 QUIC 后重测:若位置随之变正常,说明 UDP 443 绕开了住宅出口,见第十四节。
十六、公开发布前的脱敏清单
提交博客前再次搜索:
rg -n '<真实IP>|<真实UUID>|<真实公钥>|<真实ShortID>|token|password|privateKey' _posts/
公开文章中不应包含:
- Xray UUID;
- REALITY 私钥;
- Short ID;
- HomeIP 用户名、密码或订阅地址;
- Clash 订阅 URL;
- 云厂商控制台截图中的实例 ID、账单信息;
- 企业内网域名、DNS 和网段。
REALITY 公钥和服务器 IP 虽然不是传统意义上的私钥,但它们与 UUID、Short ID 组合后就是可使用的节点凭据,因此同样不应完整公开。
总结
这次部署最后得到的几个关键经验:
- Xray 安装成功不等于节点可用,配置自检、systemd、端口和真实客户端缺一不可;
- REALITY 解决主动探测和 TLS 伪装,但不会隐藏 VPS IP,也无法修复目标服务的 GeoIP 数据;
- DNS 防泄露不能依靠“多写几个 DNS”,必须按域名类别选择唯一解析路径;
- Clash Verge Rev 配置失败时可能退回最小配置,UI 仍在不代表 TUN 和 DNS 真正运行;
- 同一
/24内更换相邻 IP,通常无法改变按网段维护的 GeoIP 结果; - 当 Google 对机房 IP 的判断无法纠正时,为 Gemini 单独配置 HomeIP,比反复修改协议和 DNS 更直接;
- 最可靠的排障方法永远是分层验证:服务端、隧道、DNS、路由、浏览器、目标服务。
自建网络最大的价值不只是“能连上”,而是每一层都能解释、验证、回滚,并且知道问题究竟发生在哪里。