news 2026/10/21 18:18:03

微信校验文件不用文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信校验文件不用文件

H5 站刚迁完,HTTPS 正常,接口正常,页面也正常。接下来要接微信,第一道门槛是「业务域名」。

业务域名,是微信允许你在微信内置浏览器里直接打开的域名白名单。不在白名单里的域名,用户在微信里点开就会被微信接管,跳到它自己的一张提示页。要让微信相信这个域名归你管,它给的办法是给你一个随机文件名和一段随机内容,让你放到「网站根目录」,然后微信的服务器自己来请求一次,比对返回的内容。

这次微信给的是新版校验形式。

文件名是一串随机的 32 位十六进制字符串,加.txt,旧版是MP_verify_xxxx.txt。文件内容是一段随机的 40 位十六进制字符串。部署位置,微信的说法是「网站根目录」。校验方式是微信服务器去请求https://<你的域名>/<校验文件名>.txt,然后比对响应体。

麻烦就在这个「网站根目录」上。我这台的根目录,根本不在我能随手 touch 的地方。

微信服务器 └─> https://h5.example.com/<校验文件名>.txt └─> DNS → 宿主 443 └─> 1Panel 的 OpenResty 容器(listen 443,server_name h5.example.com) ← 请求先到这儿 └─> location ^~ / → proxy_pass http://127.0.0.1:<端口> └─> H5 容器(nginx:80,静态根 /usr/share/nginx/html/)

H5 是跑在容器里的静态站点,前面还挡着一层 1Panel 的 OpenResty。同机又跑着 6 个别人的站点,面板配置动错了是要出事故的。

微信在问的其实是:谁能控制这个域名的 HTTP 响应。

在这一层拓扑里,答案是反代配置,不是文件系统。只要我能让 OpenResty 对GET /<校验文件名>.txt返回那 40 个字节,微信就满意了。它不管这 40 个字节来自磁盘上的一个文件,还是来自配置里的一行字符串。

落地方式我数过,有四种,而且差别不小。

直接让 nginx return 文本最短,不用建文件,零路径,跟容器完全解耦,三个站点通用,换文件名时改一次配置就行。指向文件那条要建文件,配置里必须写容器内路径,同样跟容器解耦、三个站点通用,只是换文件名得同时改配置和换文件,多一步。

容器直投就是docker cp进容器,见效最快,可容器一重建文件就没了,而且只有那个有静态服务的站能这么做,多个站点得逐个 cp。

我选了第一种。理由不是它最短,而是它把「域名归属」这件事用配置直接答完了:不碰文件系统、不碰容器、也不依赖镜像构建。

配置加在该站点的 server 块里。

# 微信业务域名校验(2026-09-22) location = /<校验文件名>.txt { default_type text/plain; return 200 '<校验内容>'; }

三个站点各贴同样一段。微信的业务域名校验是按域名逐个发起的,哪个域名在微信里被拦了,就给哪个加,内容一模一样。

写这段的时候有几个点,是我从实测和文档里抠出来的。

必须精确匹配。漏掉=,写成location /<校验文件名>.txt,会被location ^~ /先命中,请求反代给后端,回 404。=后面留个空格,官方语法是location [ = | ~ | ~* | ^~ ] uri,=/路径解析器也认,但没必要冒这个险。

末尾不带换行。return输出的是字符串本体,不含末尾换行,正好 40 字节,跟微信期望的严格对上。

不需要root。return在 rewrite 阶段就返回了,后面的root、try_files根本不执行。

default_type让Content-Type是text/plain,微信只看 body,写不写都能过,显式写下来更明确。位置放哪儿都行,精确匹配=优先级最高,server 块内放哪都不会被location ^~ /吃掉。

分号别漏,return 200 '…';。

改完之后,面板保存会自动 reload,手工改的话先nginx -t再nginx -s reload。

验证的时候别省后三条。

# ① 内容与微信的请求完全一致curl-shttps://h5.example.com/<校验文件名>.txt;echo# 期望:<校验内容># ② 字节数与 Content-Typecurl-shttps://h5.example.com/<校验文件名>.txt|wc-c# 期望 40curl-s-o/dev/null-w"%{http_code} %{content_type}\n"\https://h5.example.com/<校验文件名>.txt# 期望 200 text/plain# ③ 确认主站没被改坏(同机有别人的站点,这条必须做)curl-sIhttps://h5.example.com/|head-1# 期望 200curl-s-H'Host: api.example.com'http://127.0.0.1/api/v1/products|head-c60;echo# ④ 确认配置真的加载了(这条最有价值)dockerexec<面板容器>nginx-T2>&1|grep-A3<校验文件名>

写完这段配置我心里其实是有点虚的。改的是整台机器上所有站点共用的反代。所以动配置之前先做了一次备份,而且把备份验了一遍,列得出全部域名才算成立。

BK=<备份目录>;TS=$(date+%Y%m%d_%H%M%S);mkdir-p"$BK"find/opt/1panel-name"*.conf"-typef2>/dev/null|tar-czf"$BK/openresty_all_$TS.tar.gz"-T-tar-tzf"$BK/openresty_all_$TS.tar.gz"|grep-E"example|<他人域名A>|<他人域名B>"# 列得出域名才算备份成立

这里我踩过一次。早先那条备份命令写成find … | head -1,抓到的是项目自带的 conf.d。打出来一个 1.5K 的包,里面不含任何站点配置,等于没备份。

备份这件事,不验证就等于没做。

这 4 行配置能成立,靠的是 nginx 的匹配顺序。nginx 处理请求时,location的匹配顺序是:精确匹配=先,然后是前缀匹配^~,再是正则,最后是普通前缀。1Panel 生成的站点配置,主入口是location ^~ /,它把所有请求都反代给后端容器。

而location = /xxx.txt是精确匹配,优先级最高。命中之后,return在 rewrite 阶段就把请求结束了,proxy_pass属于后面的 content 阶段,压根没机会执行。

这就是零文件的物理基础。

微信服务器 ---> OpenResty(精确匹配命中 ---> return 200) │ ✗ 不往下走:不查磁盘、不反代、不进容器 ↓ (后端容器完全无感)

不需要文件,不需要root,不需要docker cp,也不需要重新构建镜像。H5 容器以后换镜像、换版本、重建,全部无感。

要是漏掉=会怎样?

写成location /<校验文件名>.txt就是前缀匹配,按优先级排在location ^~ /后面。^~先命中,请求被反代给 H5 容器,容器里没这个文件,于是 404。

我记得这个 404 特别迷惑。语法检查nginx -t是通过的,curl站点首页也是正常的,只有校验这一个路径失败。这是这条路上最常见的翻车点。

那内容为什么必须严格 40 字节?

微信比对的是响应体。return输出字符串本体,不带末尾换行;echo会带。所以走文件路线的话,得这么写:

printf'%s''<校验内容>'><目录>/<校验文件名>.txtwc-c<目录>/<校验文件名>.txt# 期望 40

printf '%s'和echo差的那一个字节,就是校验通过与校验失败的距离。

指向文件那条我没选,还有一个原因:配置里的root必须写容器内路径。OpenResty 本身是个容器,挂载关系是宿主/opt/1panel/www对应容器/www。写成宿主路径/opt/1panel/www/sites/…,nginx 在容器里找不到,请求变成 404。而nginx -t照样通过,非常隐蔽。

容器直投那条用docker cp把文件塞进 H5 容器的静态根,大约 30 秒就能见效,但它只对有静态文件服务的那个站有效,而且容器一重建就没了。要想持久,就得把文件放进前端的public/再 commit,等于把运营期的临时校验写进了代码仓库。

微信重新校验时会换一套文件名和内容,这时候几条路的代价就不一样了。

直接 return 文本,改一次配置,换掉 location 的文件名和 return 的内容,大约 30 秒。

指向文件加精确匹配,改配置换文件名,再换一个新文件。

指向文件加正则匹配,最省事,什么都不用改,把新文件丢进那个目录就行——前提是主 location 不是^~。

容器直投最麻烦,重新 cp 一遍,要持久还得改代码、重建镜像。

正则写法是location ~ ^/[0-9a-f]{32}\.txt$这种,只在主 location 是location /的时候才可用。面板生成的是^~ /,^~优先级高于正则,会先把请求吃掉。动手前先确认一下:

docker exec <面板容器> nginx -T 2>&1 | grep -E "^\s*location" | sort -u

检查项我挨个过一遍。

备份做了没做而且验没验过,用tar -tzf去 grep 域名,列得出域名才算。语法通没通过,docker exec <面板容器> nginx -t,要回syntax is ok。

配置真加载了没,nginx -T 2>&1 | grep -A3 <校验文件名>,要回显出那段 location。

路径返回的内容对不对,curl -s https://h5.example.com/<校验文件名>.txt,要和微信给的一致。接上wc -c要是 40。状态码和类型对不对,curl -s -o /dev/null -w "%{http_code} %{content_type}",要是200 text/plain。

主站有没有被改坏,我用curl -sI https://h5.example.com/ | head -1看,要 200,接口要回 JSON。同机有别人的站点,这条必须做。站点基线变没变,用多域名Host探测,要和改动前一致。其余站点加没加,把上一条换个域名重跑,同样要返回内容。

平台侧提交校验,回公众平台点提交。微信里访问还拦不拦,用微信打开 H5,要能正常显示。若微信那个页面已经超时,回公众平台重新发起一次就行,已经部署好的内容不会失效。

微信问的是「谁控制这个域名的响应」,我能答这个问题的那一层是反代配置,跟文件系统没关系。精确匹配=是这套写法成立的前提,漏掉它就会静默 404;return不带换行,所以是严格 40 字节。备份不验证等于没备份,tar -tzf列得出域名才算成立。

写完之后我又想了一遍那个 1.5K 的假备份。要是当时没顺手tar -tzf验一下,我可能就带着它去改整台机器的反代了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 18:15:27

自建代码评审自动化流水线:从人工失效到机器兜底

1. 从“代码评审”四个字说起&#xff1a;为什么我又造了一个轮子“open-code-review”这个标题&#xff0c;乍一看像是一个开源项目名&#xff0c;实际上它更像是一类需求的统称——把代码评审这件事&#xff0c;从“靠人盯”变成“靠流程和工具兜底”。我最初动这个念头&…

作者头像 李华
网站建设 2026/10/21 18:13:13

私域运营新范式:“七新”增长模型重塑用户资产与增长战略

1. 内容整体设计与思路拆解 1.1 为什么“七新”增长模型此刻被推上台前 过去三四年&#xff0c;我见过太多品牌方在私域里反复折腾同一个问题&#xff1a;加了十几万好友、建了几百个群&#xff0c;结果销售额没见涨多少&#xff0c;反而是运营团队越做越累、用户越来越沉默、…

作者头像 李华
网站建设 2026/10/21 18:13:01

AIGC检测率过高?九款降AI率工具实测与选型指南

如果你正在读专科&#xff0c;对这套流程应该不陌生&#xff1a;AI辅助写了初稿&#xff0c;交之前顺手测一下AIGC检测率&#xff0c;弹出的数字直接让人心凉半截——38%、46%&#xff0c;甚至超过60%。熬夜改到凌晨两点的结果&#xff0c;往往只是把几个词换成同义词&#xff…

作者头像 李华
网站建设 2026/10/21 18:11:24

从零搭建轻量级代码评审工具:技术选型与核心模块实现

1. 从零搭建代码评审工具&#xff1a;为什么我要自己造这个轮子代码评审这件事&#xff0c;但凡在团队里写过几年代码的人都有一肚子话想说。大厂有专门的评审平台&#xff0c;小团队往往就靠聊天工具里甩个链接、口头说两句“帮我看看这段”&#xff0c;然后就没有然后了。我所…

作者头像 李华
网站建设 2026/10/21 18:11:08

独立DAC打造精密可调模拟信号源:选型、校准与排障全记录

前阵子做一个可调模拟信号源的时候&#xff0c;我一开始用的是定时器输出 PWM&#xff0c;再用两级 RC 低通滤成直流。调到 1kHz 以下还算能看&#xff0c;一旦想精确输出 1.234V 这种电平&#xff0c;偏差和纹波就冒出来了。后来我把方案整个换成了独立的 DAC 数模转换控制器&…

作者头像 李华