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

162 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 根路径站点验证文件
放在这个目录里的文件,会被 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`:
```powershell
# Windows 本地 → 服务器(只同步新增的验证文件)
scp "E:\Demo\Go\yunzerwebsiteallinone\go\verify\1XXl8fPHr9.txt" root@10.31.100.2:/www/wwwroot/api.yunzer.cn/verify/
```
```bash
# 服务器上确认
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)` 之前):
```nginx
# 域名校验文件(微信 / 搜索引擎 / 第三方平台)必须能用 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`,然后确认:
```bash
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 后,点微信「保存」的**同时**在另一个窗口执行:
```bash
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 走的是回环,不代表公网通。微信爬虫在公网,必须用外部验证:
```bash
# 从任意一台外部机器/手机流量执行
curl -i http://api.yunzer.cn/1XXl8fPHr9.txt # 期望 200 + 内容,且无 Location 头
```
或用手机切 4G/5G(不连 WiFi)打开 `http://api.yunzer.cn/1XXl8fPHr9.txt`,
能看到 `1XXl8fPHr9` 即为公网可达。