Files
yunzerwebsiteallinone/go/verify
2026-10-31 19:17:37 +08:00
..
2026-10-31 19:17:37 +08:00
2026-10-31 19:17:37 +08:00

根路径站点验证文件

放在这个目录里的文件,会被 Go 服务直接吐到域名根路径,用于各类「域名所有权 / 站点所有权」验证:

  • 备案与域名所有权验证:1XXl8fPHr9.txt(阿里云、腾讯云)
  • 搜索引擎站点验证:googleXXXX.html、msXXXXXXXX.txt、BingSiteAuth.xml
  • 第三方平台域名校验:各平台下发的 .txt / .html

为什么需要这个目录

线上 nginx 对所有域名都是「全量反代给 Go」(location / → proxy_pass), Go 不认识的路径一律回 beego 404 页,所以放在服务器网站根目录里的验证文件根本取不到。 改成由 Go 统一托管后,不再依赖 nginx 规则 —— 宝塔重生成 vhost 也不会被冲掉。

实现见 go/routers/verifyfiles.go(过滤器)与 go/pkg/verifyfiles/(路径解析与安全校验)。

生效规则

  • 只处理 GET / HEAD;
  • 只认根路径下的单段文件名(如 /1XXl8fPHr9.txt),不支持子目录;
  • 文件名必须形如「字母或数字开头 + 字母数字与 . _ -」,长度 ≤ 64;
  • 扩展名必须在允许列表内(默认 .txt / .html / .htm);
  • 文件必须真实存在,否则放行给正常路由(最终仍是 beego 404,行为不变)。

配置项在 app.conf:

配置 说明 默认值
verify_dir 验证文件目录 <服务根目录>/verify
verify_exts 允许的扩展名,逗号分隔 .txt,.html,.htm
verify_hosts 限定生效域名,逗号分隔(不含端口) 空 = 所有走本服务的域名

「服务根目录」= conf/app.conf 所在目录的上一级。 线上即 /www/wwwroot/api.yunzer.cn(systemd 的 WorkingDirectory), 所以验证文件目录是 /www/wwwroot/api.yunzer.cn/verify。 目录跟着 app.conf 走,不管用 go run main.go 还是编译后的二进制、 不管从哪个目录启动,结果都一致。

新增一个验证文件

  1. 把平台给的验证文件放进本目录(文件名保持原样,内容不要加多余字符);
  2. git add && git commit && git push;
  3. 服务器上 git pull 后重启 Go 服务:systemctl restart go-api(或 bash scripts/service.sh restart);
  4. 访问 https://api.yunzer.cn/1XXl8fPHr9.txt 验证。

注意:本目录下的 .md 等非白名单扩展名不会被吐出去,README 不会被当成静态文件泄露。

部署注意:本仓库(allinone)与服务器不是同一个仓库

排查问题时确认过,线上是两套独立的 git 仓库,别混用:

位置 角色
源仓库 本地 yunzerwebsiteallinone(远端 10.31.100.4:8418/.../yunzerwebsiteallinone.git) 唯一真相源,go/ 只是其中一个子目录
部署目录 服务器 /www/wwwroot/api.yunzer.cn 只放 go/ 的内容,里面是另一个 Go 仓库(HEAD 与 allinone 不一致,例如 269fbd0)

所以:

  • allinone 的 master 与服务器仓库的 master 是两条线,git pull 拉不到对方;
  • 部署 = 把 go/ 的内容同步到服务器目录(手工拷文件 / scp / rsync),新增目录最容易漏—— 本目录就是漏传过一次导致验证 404 的典型;
  • 同步本目录用文件路径,别用目录路径,否则会多出一层 verify/verify:
# Windows 本地 → 服务器(只同步新增的验证文件)
scp "E:\Demo\Go\yunzerwebsiteallinone\go\verify\1XXl8fPHr9.txt" root@10.31.100.2:/www/wwwroot/api.yunzer.cn/verify/
# 服务器上确认
ls -l /www/wwwroot/api.yunzer.cn/verify/
curl -i http://127.0.0.1:9000/1XXl8fPHr9.txt    # 直连 Go,绕过 nginx

Go 每次请求都会实时 os.Stat 验证目录,文件放进去立即生效,不需要重启服务。

⚠️ 本项目 nginx 的两个既定事实(改配置时别踩)

api.yunzer.cn 的 vhost(/www/server/panel/vhost/nginx/api.yunzer.cn.conf)有两个特点, 直接影响本机制的配置方式:

  1. proxy_set_header Host 10.31.100.2; —— 转发给 Go 时 Host 头被改成了 IP, Go 侧看不到 api.yunzer.cn 这个域名。 → 因此 verify_hosts 必须留空(留空 = 所有走本服务的域名都放行)。 一旦填了 api.yunzer.cn,Go 会因为 Host 对不上而拒绝提供文件。
  2. location ^~ / { proxy_pass ... } —— ^~ 匹配后 nginx 不再检查正则 location, 后面那两条「禁止访问的敏感文件 / 敏感目录」的 location ~* 规则在这个站点实际不生效。 目前没有泄露风险(Go 侧不提供工作目录文件,未知路径一律 404),但要知道这层保护是空的。

⚠️ 微信 / 小程序业务域名校验:必须让 http 也能直接访问

微信公众平台配置「业务域名」时,会去 http:// 你的域名根目录抓取校验文件 (http://api.yunzer.cn/1XXl8fPHr9.txt)。宝塔默认的 HTTP→HTTPS 强制跳转会把 这个请求 301 到 https,微信拿到重定向就直接判「校验文件验证失败」。

症状:浏览器访问 https://域名/校验文件.txt 完全正常,微信后台却报 「校验文件验证失败,请确认已在域名根目录正确放置校验文件」。

修法:宝塔 → 网站 → 对应站点 → 设置 → 配置文件,在 #HTTP_TO_HTTPS_START 块里 加一条例外(放在 if ($isRedcert != 1) 之前):

# 域名校验文件(微信 / 搜索引擎 / 第三方平台)必须能用 http 直接访问,不能 301 跳 https
if ( $uri ~ "^/[A-Za-z0-9._-]+\.txt$" ) {
    set $isRedcert 1;
}

注意:这条必须加在 HTTP_TO_HTTPS 块里。在 location 里加 location = /xxx.txt 没用——server 层的 rewrite 在 location 匹配之前就执行了,重定向照样发生。

改完 nginx -t && nginx -s reload,然后确认:

curl -i http://api.yunzer.cn/1XXl8fPHr9.txt    # 期望 200 + 文件内容,且没有 Location 头
curl -i https://api.yunzer.cn/1XXl8fPHr9.txt   # 仍是 200

其它常见原因(按排查顺序):

  1. 文件必须原样:文件名区分大小写、不能改后缀;内容与微信下载的那份字节级一致, 不能带 BOM / 结尾换行。xxd verify/xxx.txt 确认没有 efbbbf 开头、没有结尾 0a。
  2. 备案要同步到微信侧:截图里写明「新备案域名需 24 小时后才可配置」,工信部数据同步 到微信侧通常 24~48 小时;备案主体须与小程序主体一致,接入商须与服务器提供商一致。 部分省份还要求已完成公安联网备案。
  3. 微信侧缓存:全部正确仍失败时,重新点「下载文件」换一个随机文件名放进 verify/, 再保存,可绕过平台短时缓存。
  4. 小程序主体类型:业务域名只支持企业主体小程序,个人主体 / 海外类型小程序 在微信侧根本不支持配置业务域名(报错形态与校验失败一样)。
  5. 修改次数限制:服务器域名每月只能改 5 次,业务域名至多 20 个,别反复试。

判定要点:如果访问日志里能看到微信爬虫(UA 类似 Mozilla/4.0)请求校验文件并返回 200, 说明文件校验已通过,剩下的失败只能是备案 / 主体 / 缓存这类平台侧原因, 不要再改服务器配置。

用访问日志判读卡在哪一步

改完 nginx 后,点微信「保存」的同时在另一个窗口执行:

tail -f /www/wwwlogs/api.yunzer.cn.log | grep -i "1XXl8fPHr9\|MP_verify"
日志表现 卡在哪 怎么办
新增一行,状态 200 文件链路正常 剩下是备案同步 / 微信缓存,等 24~48 小时或换文件名重试
新增一行,状态 404 微信要的文件名和你放的不一致 重新「下载文件」,原文件原样传进 verify/
新增一行,状态 301 nginx 例外没生效 检查 if ( $uri ~ ^/[A-Za-z0-9._-]+[.]txt$ ) 是否加在 if ($isRedcert != 1) 之前
一行都没有 微信压根没来拉 微信在备案校验阶段就拦下了,与文件无关 → 等备案同步,核对主体/接入商

公网可达性自查

服务器本机 curl 走的是回环,不代表公网通。微信爬虫在公网,必须用外部验证:

# 从任意一台外部机器/手机流量执行
curl -i http://api.yunzer.cn/1XXl8fPHr9.txt    # 期望 200 + 内容,且无 Location 头

或用手机切 4G/5G(不连 WiFi)打开 http://api.yunzer.cn/1XXl8fPHr9.txt, 能看到 1XXl8fPHr9 即为公网可达。