gstatic连接问题导致Google Gemini / Studio页面乱码或图标缺失问题

1 阅读6分钟

现象

最近用自己搭的代理访问 Google Gemini 和 AI Studio 的时候,页面总是花的——侧边栏的文字缺胳膊少腿,图标全变成了方块或者乱码字符,有时候甚至整个页面只剩个白板。

chrome_XHBKoIlUTu(1).png

打开 F12 一看,控制台有红色的 ERR_CONNECTION_CLOSED,失败的请求清一色指向 www.gstatic.comfonts.gstatic.com

chrome_krzqt0Ogkc.png

而且换个节点就好了,说明不是 Google 的锅,问题出在我自己的链路上。

初步排查

先去论坛搜了一圈,发现不少人遇到过类似的问题。核心结论是:Gemini 的 UI 重度依赖 gstatic.com 上的静态资源(字体、图标、CSS、JS),这些资源加载不出来,页面自然就炸了。 linux do

但大多数帖子的解决方案是"换个节点"或者"在客户端路由里把 gstatic.com 加上"——我试了,没用。

定位根因

既然客户端路由没问题(MATCH,Proxy 兜底规则已经把所有非国内流量都走了代理),那问题只可能出在服务端。

SSH 上去,直接 curl 一下:

curl -sI --max-time 5 https://www.gstatic.com
# 超时,没有任何返性

果然连不上。再看看 DNS 解析的结果:

dig +short www.gstatic.com
# 203.208.50.162

这个 IP 一查,属于 Google 的亚太节点。一台在美国的 VPS,DNS 解析出来一个亚洲的 IP,而且还连不上——这就很离谱了。

用 Cloudflare 的 DoH 验证一下:

curl -sH 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=www.gstatic.com&type=A'
# 返回 142.251.46.227

这是个正常的美国 Google IP。手动指定这个 IP 去连:

curl -sI --max-time 5 \
  --resolve www.gstatic.com:443:142.251.46.227 \
  https://www.gstatic.com
# HTTP/2 404  ← 正常!gstatic 根路径本来就是 404

破案了:系统 DNS 返回了错误的 IP。

我这台 VPS 的 /etc/resolv.conf 用的是 8.8.8.8,但实际解析出来的 gstatic.com IP 跟 Cloudflare DoH 拿到的完全不一样。具体原因可能是 DNS 查询在链路上被污染了,或者上游解析策略有问题,总之拿到了一个从美国根本连不上的亚太 IP。

解决方案

知道了原因,修起来就简单了。分两步:

1. 修复系统 DNS

/etc/resolv.conf8.8.8.8 改成 1.1.1.1。验证了一下,Cloudflare 的 DNS 对 gstatic.com 返回的是正确的 IP:

# 修改前
nameserver 8.8.8.8
nameserver 8.8.4.4
​
# 修改后
nameserver 1.1.1.1
nameserver 1.0.0.1

改完验证:

dig +short www.gstatic.com
# 142.250.191.35  ← 正确的美国 IP

⚠️ 如果你的 VPS 是 SolusVM 面板管理的,resolv.conf 可能会在重启后被覆盖。可以用 chattr +i /etc/resolv.conf 锁定文件,或者通过面板修改 DNS 设置。

2. 给 Xray 加上 DoH DNS + 嗅探(双保险)

光改系统 DNS 能解决大部分场景,但为了更稳,我在 Xray 的配置里也加了一层保障。

核心改动有三个地方:

a) 内置 DNS,Google 域名走 DoH

"dns": {
  "servers": [
    {
      "address": "https+local://cloudflare-dns.com/dns-query",
      "domains": [
        "gstatic.com",
        "googleusercontent.com",
        "googleapis.com",
        "google.com"
      ]
    },
    "1.1.1.1"
  ]
}

指定 Google 相关的域名走 Cloudflare DoH 解析,其余域名走 1.1.1.1。DoH 走的是 HTTPS,不会被中间链路篡改。

b) 入站开启嗅探

"sniffing": {
  "enabled": true,
  "destOverride": ["http", "tls", "quic"]
}

这一步很关键。如果你的客户端用了 fake-ip 模式(比如 Clash 的默认 DNS 配置),发到服务端的目标地址其实是一个假 IP(类似 198.18.0.x),而不是真实域名。

开了嗅探之后,Xray 会从 TLS 握手的 SNI 字段里提取出真实域名(比如 www.gstatic.com),然后再用上面配置的 DoH 去解析成正确的 IP。

不开嗅探的话,前面配的 DNS 规则根本不会生效——因为 Xray 拿到的只是一个 IP 地址,匹配不到任何域名规则。这个坑我踩了好几遍才意识到。

c) freedom 出站加 UseIP

{
  "protocol": "freedom",
  "tag": "direct",
  "settings": {
    "domainStrategy": "UseIP"
  }
}

告诉 freedom 出站:碰到域名的时候,先用 Xray 内置的 DNS 解析成 IP,再去连接。不加这个的话,freedom 默认用系统 DNS 解析,又会拿到那个错误的 IP。

完整配置

最终的 Xray 配置长这样:

{
  "log": {
    "loglevel": "warning",
    "access": "/var/log/xray/access.log",
    "error": "/var/log/xray/error.log"
  },
  "dns": {
    "servers": [
      {
        "address": "https+local://cloudflare-dns.com/dns-query",
        "domains": [
          "gstatic.com",
          "googleusercontent.com",
          "googleapis.com",
          "google.com"
        ]
      },
      "1.1.1.1"
    ]
  },
  "inbounds": [
    {
      "port": 10086,
      "listen": "127.0.0.1",
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "你的UUID",
            "level": 0
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
          "path": "/你的路径"
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls", "quic"]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct",
      "settings": {
        "domainStrategy": "UseIP"
      }
    }
  ]
}

改完 systemctl restart xray,清一下浏览器缓存,刷新 Gemini——图标回来了,世界清净了。

如果你没法改服务端配置怎么办?

上面的方案是在服务端解决问题。但如果你用的是第三方现成的服务,改不了服务端配置,在客户端侧也有一些办法:

方法一:本地 hosts 文件强制指定正确 IP

最暴力也最直接。手动把 gstatic.com 的正确 IP 写进系统 hosts 文件:

# Windows: C:\Windows\System32\drivers\etc\hosts
# macOS/Linux: /etc/hosts

142.250.80.3 www.gstatic.com
142.250.80.3 fonts.gstatic.com
142.250.80.3 ssl.gstatic.com
142.251.46.227 googleusercontent.com

正确的 IP 可以通过 DoH 查询获取:

https://cloudflare-dns.com/dns-query?name=www.gstatic.com&type=A

缺点:Google 的 IP 会变,过一段时间可能要重新查一次。但一般 Google CDN 的 IP 比较稳定,能撑挺久。

方法二:让 gstatic.com 走直连而不是代理

有些情况下,gstatic.com 从国内其实是能直连的(取决于你所在地区和运营商)。可以在客户端的路由规则里把它设成 DIRECT:

Clash 配置示例:

rules:
  - DOMAIN-SUFFIX,gstatic.com,DIRECT
  - DOMAIN-SUFFIX,googleusercontent.com,DIRECT
  # ... 其他规则

原理:绕过有问题的代理服务器,让这些静态资源请求直接从你本地出去。gstatic.com 本身只是字体和 JS 文件,不涉及账号数据,直连问题不大。

缺点:不是所有地区都能直连 gstatic.com。试一下,能访问 https://fonts.gstatic.com 就说明能用。

方法三:客户端 DNS 改用 DoH

如果你用的是 Clash Meta (Mihomo),可以在 DNS 配置里把 Google 相关域名的解析指定走 DoH:

dns:
  enable: true
  enhanced-mode: fake-ip
  nameserver:
    - https://doh.pub/dns-query
    - https://dns.alidns.com/dns-query
  nameserver-policy:
    "+.gstatic.com": "https://cloudflare-dns.com/dns-query"
    "+.googleapis.com": "https://cloudflare-dns.com/dns-query"
    "+.googleusercontent.com": "https://cloudflare-dns.com/dns-query"

nameserver-policy 可以针对特定域名指定 DNS 服务器。不过这种方式只能保证客户端侧解析正确,如果问题出在代理服务器的出口 DNS 上(就像我遇到的情况),客户端改 DNS 可能帮不上忙——因为最终请求还是从服务端出去,服务端会重新解析一次。

方法四:换个节点

没什么技术含量,但确实管用。不同节点的服务器 DNS 配置不一样,换一个可能就好了。如果你发现某个节点一直访问 Gemini 乱码,大概率就是那台服务器的 DNS 有问题,反馈给服务商让他们去修。

排查思路总结

回头看,这次排查绕了不少弯路。核心教训:

  1. 客户端路由规则没问题 ≠ 服务端能连上目标。一开始在客户端侧折腾了半天,加了各种 DOMAIN-SUFFIX 规则,其实根本不是客户端的锅。
  2. DNS 污染不只发生在客户端。服务端的 DNS 一样可能返回错误结果,尤其是用 UDP 明文 DNS 的时候。
  3. fake-ip + 不开嗅探 = DNS 配置白搭。这个组合是最大的坑。客户端用 fake-ip 模式发过来的是 IP 不是域名,服务端不嗅探就不知道真正要访问的是啥,配再多域名规则也没用。
  4. 优先用 DoH/DoT。UDP 53 端口的明文 DNS 太容易被中间人篡改了。花两分钟配个 DoH,能省很多排查时间。

如果你也遇到了类似的"某些 Google 服务页面乱码、静态资源加载失败"的问题,不妨先 SSH 到服务端 dig 一下对应域名,很可能就是 DNS 的锅。