Simple AI Chat with Cloudflare Workers
这是一篇记录从零打造并稳定上线「高粱米 AI 姐姐」聊天系统的实战总结。项目基于 Hexo + NexT,前端纯静态,通过 Cloudflare Workers 作为安全代理完成与 OpenAI 兼容接口的对接。
目标与约束
- 前端纯静态,不暴露任何密钥
 - 流式输出、连续对话、轻量 UI
 - 具备速率限制和来源校验,防滥用
 - GitHub Pages 自动部署,故障可快速回滚
 
架构设计
- 前端:
source/js/chat.js- 通过 
STATE.globalConfig.proxyUrl统一指向 Worker 代理 - 两条路径:非流式与流式(SSE),均走代理
 - 本地开发:推荐使用 
wrangler dev启动本地 Workers 代理;前端不再注入 API Key(不再支持直连) 
 - 通过 
 - 代理:Cloudflare Worker 
chat-proxy- 环境变量:
CHAT_API_KEY、ALLOWED_ORIGINS - 速率限制(KV 可选):每小时/每日
 - CORS 处理,阻断非白名单来源
 - 仅允许受控 model 列表
 
 - 环境变量:
 
核心演进与坑点
- 代理地址与优先级
 
- 现象:控制台仍然请求旧域名
 - 根因:PJAX + 浏览器缓存 + localStorage 旧配置覆盖
 - 方案:代码优先使用 
global.proxyUrl,并给chat.js增加版本号参数强制刷新 
- Service Worker 安装失败
 
- 现象:
addAll404、installing worker became redundant - 根因:GitHub Pages 上的路径不一,预缓存名单失配
 - 方案:更换为极简透传 SW(只 skipWaiting/clients.claim,不做缓存拦截)
 
- UI 可拖拽“漂移”
 
- 现象:入口气泡可被拖到奇怪位置、记忆后影响体验
 - 方案:默认关闭拖拽(
entryBubbleDraggable: false),需要时再手动开启 
- CORS 与来源校验
 
- 现象:
Failed to fetch/Origin not allowed - 方案:Worker 中检查 
Origin是否包含https://zhu-jl18.github.io(或其它白名单),OPTIONS 预检返回 200 + CORS 头 
- 清理废弃的 API Base / API Key 前端配置
 
- 现象:设置面板里仍能看到 API Base / API Key,但实际请求已统一走 Worker 代理
- 方案:移除前端的 API Base / API Key 字段与本地直连逻辑(
CHAT_LOCAL_*),统一使用proxyUrl;chat.js已精简设置面板为仅选择模型 
 - 方案:移除前端的 API Base / API Key 字段与本地直连逻辑(
 
本地开发与测试(统一代理)
前置环境
- 启动 Hexo 本地服务(默认 http://localhost:4000):
1
npm run server
 - 启动 Cloudflare Worker 本地代理(默认 http://127.0.0.1:8787):
1
2cd cloudflare-worker
wrangler dev 
- 启动 Hexo 本地服务(默认 http://localhost:4000):
 指定本地代理地址(两选一)
- 使用浏览器控制台设置持久覆盖(推荐):
1
2localStorage.setItem('chat-proxy-override', 'http://127.0.0.1:8787');
location.reload(); - 临时变量(刷新后失效):
1
window.CHAT_PROXY_DEV = 'http://127.0.0.1:8787';
 
- 使用浏览器控制台设置持久覆盖(推荐):
 验证
- 打开网页 → 开发者工具 → Network,确认聊天请求发往 
127.0.0.1:8787。 - 代理会校验 
Origin。当前chat-proxy.js默认允许localhost:4000/127.0.0.1:4000;若需更严格控制,可在 Cloudflare Dashboard 的ALLOWED_ORIGINS中显式加入本地地址。 - 预检(OPTIONS)应返回 200,并带有 CORS 头(见 
handleCORS())。 
- 打开网页 → 开发者工具 → Network,确认聊天请求发往 
 恢复生产代理
1
2localStorage.removeItem('chat-proxy-override');
location.reload();
部署与回滚
- 分支:
develop开发 → PR →main自动部署(GitHub Actions) - 缓存处置:新版本发布后,给关键脚本带版本号;必要时在浏览器 DevTools 中清理 SW 与缓存
 - 回滚:强制推送 
main到上一稳定提交(紧急时使用) 
速查清单
- Worker 环境变量:
CHAT_API_KEY:真实 API KeyALLOWED_ORIGINS:https://zhu-jl18.github.io
 - 快速探活:
1
curl -I https://chat-proxy.nontrivial2025.workers.dev
 - 前端清本地配置:
1
localStorage.removeItem('chatcfg.v2')
 - 常见报错:
ERR_BLOCKED_BY_CLIENT:被广告拦截插件拦截,非功能性错误- SW 404:使用极简 SW;若安装失败,先 Unregister 再刷新
 - 仍走旧域名:确认脚本已带版本号、清理缓存
 
 
致谢
- 开发:Mako & Cascade(级联的含义是把复杂问题拆成有序步骤,像瀑布一样推进)
 - 特别感谢每次“线上 real-time 排障”的耐心与果断,这个系统在不断打磨中变得越来越稳。