这篇记录一下今天把「路图」微信小程序正式接到微信云托管上的过程。
事情本身看起来不复杂:备案好了,域名也有了,后端服务也已经跑在云托管里。按理说只要把小程序里的 API 域名改成正式域名,再把证书传上去,就可以收工。
但真实世界当然没有这么体贴。最后真正花时间的,不是写业务代码,而是搞清楚几个平台之间到底谁管谁:微信小程序、微信云托管、腾讯云 CloudBase、DNSPod、acme.sh、微信开发者工具,各自都长得很像“腾讯系”,但权限和接口完全不是一回事。
先把架构说清楚
路图现在的结构是:
- 前端是微信小程序代码,在本地
apps/miniprogram里开发,最后上传到微信小程序平台; - 后端是 Bun/Hono API,部署在微信云托管的
prod环境里,服务名叫map-creator; - 正式 API 域名是
https://map-app.flandre.ltd; - WebSocket 域名是
wss://map-app.flandre.ltd。
小程序前端不是部署到我自己的服务器上。用户打开小程序时,微信客户端下载的是小程序包;小程序再去请求微信云托管上的后端 API。
这句话很重要。因为我一开始很容易把“前端部署在哪”和“后端部署在哪”混在一起。Web 项目常见的是前端部署到 CDN 或服务器,后端部署到另一个服务。但微信小程序不是这样,它的前端发布渠道就是微信平台本身。
第一个坑:tcb 不是这次要用的入口
我一开始自然想到用 tcb login。毕竟云托管、CloudBase、腾讯云开发,这几个名字看起来像是一家人。
结果登录之后看到的是另一个环境,里面没有我截图里的 prod,也没有 map-creator 服务。这个时候如果继续沿着 tcb 往下查,会越来越离谱。
最后确认:这次要用的是微信云托管 CLI,也就是 wxcloud。它用小程序 AppID 和微信云托管里生成的 CLI 私钥登录,能看到正确的环境:
prod
prod-d8g0ydptv00f53d5b
map-creator
这个区分以后要记住:
tcb:更偏腾讯云 CloudBase 入口;wxcloud:这次微信云托管小程序环境的正确入口。
看起来只是一个 CLI 选择问题,但它其实决定了后面所有自动化能不能跑通。
第二个坑:公网访问开了,不代表自定义域名证书就全好了
云托管服务里有一个“公网访问”的开关。它开了以后,默认域名可以访问。但默认域名有测试期限制,而且正式小程序也不应该依赖这种临时域名。
真正要让小程序稳定访问,需要把 map-app.flandre.ltd 绑定到云托管的 HTTP Service Route,并配置 HTTPS 证书。
最后验证下来,域名层正常的标准很明确:
curl https://map-app.flandre.ltd/api/health
# {"success":true,"data":{"status":"ok"}}
WebSocket 也要单独测:
curl -i
-H 'Connection: Upgrade'
-H 'Upgrade: websocket'
-H 'Sec-WebSocket-Version: 13'
-H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=='
'https://map-app.flandre.ltd/api/ws?sessionId=probe'
看到 101 Switching Protocols,才说明 WebSocket 这层也通了。
只测 HTTP 200 不够。小程序生成地图过程用到了 WebSocket,所以必须确认
wss://也能升级成功。
第三个坑:证书自动续签不是续到服务器就结束
我之前服务器上已经有 acme.sh,能给 map-app.flandre.ltd 申请 Let's Encrypt 证书。问题在于:微信云托管用的是它自己网关上的证书,不会因为我服务器上的 PEM 文件更新,就自动跟着更新。
所以完整链路应该是:
ssh tencent服务器上的 acme.sh 定时续签证书;- 续签成功后执行 reload hook;
- hook 读取新的
fullchain.pem和privkey.pem; - 调用微信云托管接口
UploadCert上传证书; - 拿到新的
CertId后调用ModifyHTTPServiceRoute; - 把
map-app.flandre.ltd这条路由切到新证书。
最后我在服务器上放了一个脚本:
/usr/local/bin/deploy-map-app-wxcloud-cert
acme.sh 的 reload command 会调用它,日志写到:
/var/log/map-app-wxcloud-cert.log
这样以后不是“服务器证书自动续签”,而是“服务器证书续签后,微信云托管域名证书也自动更新”。这才算真正闭环。
第四个坑:开发者工具的模拟器失败,不等于小程序访问失败
今天还遇到一个很迷惑的画面:微信开发者工具里显示“模拟器启动失败”。这种错误很容易让人以为是后端域名、HTTPS 或 WebSocket 又坏了。
但其实不是。这个错误发生在模拟器还没跑起来的时候,小程序代码可能根本还没执行,更谈不上已经发起网络请求。
所以判断顺序应该是:
- 先用 curl 验证后端 API 和 WebSocket;
- 再确认微信公众平台的小程序合法域名;
- 最后才看小程序代码里的请求逻辑。
合法域名这里至少要配:
request:https://map-app.flandre.ltdsocket:wss://map-app.flandre.ltd- 如果后续图片下载报错,再补
downloadFile:https://map-app.flandre.ltd
最后沉淀成一个 Codex skill
这次配置过程里,真正有价值的不是“我点了哪里”,而是把这套判断路径沉淀下来:
- 什么时候用
wxcloud,什么时候不要被tcb带偏; - 怎么确认微信云托管服务和域名真的通了;
- 怎么区分 DevTools 模拟器问题和真实网络问题;
- 怎么把 acme.sh 的证书续签接到微信云托管的证书更新;
- 哪些东西绝对不能写进仓库,比如 AppSecret、CLI 私钥、数据库密码、API key。
所以我顺手把它整理成了一个 Codex skill:lutu-wechat-cloudrun。以后只要继续处理路图小程序、微信云托管、自定义域名、证书续签之类的问题,就不用从头把这套关系重新想一遍。
我越来越觉得,很多“部署问题”本质上不是技术难,而是边界不清:谁负责前端、谁负责后端、谁负责域名、谁负责证书、谁负责自动化。边界一清,问题就开始变小。
这次比较舒服的一点是,最后不是手工点完控制台就算结束,而是把证书续签这件事真正接成了自动化。小程序可以继续请求同一个域名,后端在微信云托管里跑,证书到期前服务器会自动续签并同步到云托管。
下一步就是把小程序合法域名、真机调试和发布流程再走一遍。等这条链路稳定了,路图就可以更像一个正经产品,而不是一堆本地工具和临时域名拼出来的实验品了。