mirror of
https://github.com/SMNETSTUDIO/WeChat-AI.git
synced 2026-08-12 22:23:42 +08:00
194 lines
7.4 KiB
Markdown
194 lines
7.4 KiB
Markdown
# Cloudflare Business 缓存适配
|
||
|
||
本项目源站(Fastify Docker)已输出 **Cloudflare 友好** 的缓存头。你只需在 Cloudflare Dashboard 配好 **Cache Rules** 与区域级开关,即可最大化边缘命中。
|
||
|
||
相关代码:
|
||
|
||
| 能力 | 位置 |
|
||
|------|------|
|
||
| HTML / OG 内存缓冲 + ETag | `apps/api/src/static-pages.ts`, `index.ts` |
|
||
| 统一 Cache-Control | `apps/api/src/cache-headers.ts` |
|
||
| 公开表情 CDN | `GET /cdn/s/:id?v={content_hash}` |
|
||
| 默认 API `private, no-store` | `apps/api/src/routes.ts` `onSend` |
|
||
| CORS 白名单 | `CORS_ORIGINS` + `PUBLIC_BASE_URL` |
|
||
|
||
**本阶段不上 R2**;公开表情仍由源站 Redis blob 提供,靠边缘长缓存降压。
|
||
|
||
---
|
||
|
||
## 1. 源站响应策略(已实现)
|
||
|
||
| 路径 | Cache-Control | Cloudflare-CDN-Cache-Control | 鉴权 |
|
||
|------|---------------|------------------------------|------|
|
||
| `/` `/docs` | `public, max-age=300` | `max-age=3600, swr=86400` | 无 |
|
||
| `/app` `/admin` | `public, max-age=60` | `max-age=3600, swr=86400` | 无(壳静态;数据走 API) |
|
||
| `/og.jpg` | `public, max-age=86400, immutable` | `max-age=604800` | 无 |
|
||
| `/cdn/s/:id?v=` | `public, max-age=31536000, immutable` | 同左 | **无**;仅 public+approved+enabled |
|
||
| `/api/v1/auth/config` | `private, no-store` | — | 无(登录方式开关需实时) |
|
||
| `/health` | `no-store` | — | 无 |
|
||
| 其余 `/api/v1/**` | `private, no-store` | — | Cookie 会话 |
|
||
| 鉴权表情图 | `private, no-store` | — | 登录 / admin |
|
||
|
||
HTML 带 `ETag` + `Cache-Tag: html-shell`;公开表情带 `Cache-Tag: sticker-{id}`(便于定向 Purge)。
|
||
|
||
---
|
||
|
||
## 2. DNS / SSL
|
||
|
||
1. 域名 A/AAAA/CNAME 橙云代理到源站
|
||
2. SSL/TLS → **Full (strict)**
|
||
- 源站用有效证书,或 [Cloudflare Origin CA](https://developers.cloudflare.com/ssl/origin-configuration/origin-ca/)
|
||
3. Always Use HTTPS = On
|
||
4. 源站 `.env`:
|
||
- `PUBLIC_BASE_URL=https://你的域名`
|
||
- `COOKIE_SECURE=true`
|
||
- `LINUXDO_REDIRECT_URI=https://你的域名/api/v1/auth/callback`
|
||
- 可选 `CORS_ORIGINS=https://你的域名`(默认已含 PUBLIC_BASE_URL origin)
|
||
|
||
---
|
||
|
||
## 3. Cache Rules(按顺序创建,先匹配先生效)
|
||
|
||
路径:**Caching → Cache Rules**(Business 推荐用 Cache Rules,而不是旧 Page Rules)。
|
||
|
||
### Rule 1 — Bypass 动态 API
|
||
|
||
- **Name:** `bypass-api-private`
|
||
- **When:**
|
||
`starts_with(http.request.uri.path, "/api/v1/")`
|
||
或方法为 `POST` / `PUT` / `PATCH` / `DELETE`
|
||
- **Then:** Cache eligibility = **Bypass cache**
|
||
|
||
### Rule 2 — HTML 壳 Cache Everything + 忽略 Cookie
|
||
|
||
- **Name:** `cache-html-shells`
|
||
- **When:**
|
||
`http.request.uri.path in {"/" "/app" "/docs" "/admin"}`
|
||
- **Then:**
|
||
- Eligible for cache
|
||
- Edge TTL: **Respect origin**(或 Override 1 hour)
|
||
- Browser TTL: Respect origin
|
||
- **Cache key → Ignore query string**(可选)
|
||
- **Cookie handling → Ignore presence of cookies**(**关键**:否则带 `wa_session` 永远 MISS)
|
||
|
||
### Rule 3 — OG 图
|
||
|
||
- **Name:** `cache-og`
|
||
- **When:** `http.request.uri.path eq "/og.jpg"`
|
||
- **Then:** Eligible for cache;Edge TTL Respect origin 或 7d;**Ignore cookies**
|
||
|
||
### Rule 4 — 公开表情 CDN
|
||
|
||
- **Name:** `cache-cdn-stickers`
|
||
- **When:** `starts_with(http.request.uri.path, "/cdn/s/")`
|
||
- **Then:** Eligible for cache;Edge TTL Respect origin;**Ignore cookies**
|
||
- Cache key **保留 query string**(`v=` 内容哈希)
|
||
|
||
### Rule 5 — ~~auth/config 短缓存~~(已移除)
|
||
|
||
> `/api/v1/auth/config` 现返回 `no-store`(登录方式开关需实时生效,不应缓存)。Cloudflare 默认尊重源站 `Cache-Control`,无 `public` 的响应不进共享缓存,故该路径**无需**任何缓存规则。
|
||
|
||
### 默认
|
||
|
||
未匹配规则时:尊重源站 `Cache-Control`;无 `public` 的不进共享缓存。
|
||
|
||
---
|
||
|
||
## 4. 区域级推荐(Business)
|
||
|
||
| 设置 | 建议 |
|
||
|------|------|
|
||
| HTTP/3 (QUIC) | On |
|
||
| Brotli | On |
|
||
| Tiered Cache | On |
|
||
| Early Hints | 可选(当前 HTML 内联,收益有限) |
|
||
| Auto Minify | **Off**(避免改 HTML 导致 ETag 与部署不一致) |
|
||
| Rocket Loader | **Off** |
|
||
| Polish | 可选;若开 WebP,注意 `Accept` 变体,或只对 `/cdn/s/*` 试 |
|
||
| Mirage | 可选(移动列表) |
|
||
| Cache Reserve | 可选(长尾表情) |
|
||
| Argo Smart Routing | 源站距用户远时可选 |
|
||
| WAF Managed Rules | On |
|
||
| Rate limiting | 建议:`/api/v1/auth/*`、try-chat、上传 POST |
|
||
| Bot Fight | 按需;勿误伤 OAuth 回调 |
|
||
|
||
---
|
||
|
||
## 5. 部署后刷新缓存
|
||
|
||
| 变更 | 做法 |
|
||
|------|------|
|
||
| 新版本 HTML(app/admin/docs/landing) | Purge by URL:`/` `/app` `/docs` `/admin`,或 Purge by Tag:`html-shell` |
|
||
| 换图表情 | **无需 purge**:`?v={content_hash}` 自动新键 |
|
||
| 下架 / 拒绝公开表情 | 源站 404;边缘可能仍 HIT 至 TTL → Purge URL 或 Tag `sticker-{id}` |
|
||
| 紧急全站 | Purge Everything(会短暂升高源站压力) |
|
||
|
||
API Purge 示例(可选):
|
||
|
||
```bash
|
||
# 需要 Zone ID + API Token (Cache Purge 权限)
|
||
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
|
||
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
--data '{"files":["https://你的域名/","https://你的域名/app","https://你的域名/docs","https://你的域名/admin"]}'
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 验收 curl
|
||
|
||
```bash
|
||
HOST=https://你的域名
|
||
|
||
# HTML:第二次应 CF-Cache-Status: HIT(即使带假 cookie)
|
||
curl -sI "$HOST/" | grep -iE 'cf-cache-status|cache-control|etag|content-encoding'
|
||
curl -sI "$HOST/app" -H "Cookie: wa_session=fake" | grep -i cf-cache-status
|
||
|
||
# 公开表情
|
||
curl -sI "$HOST/cdn/s/$STICKER_ID?v=$HASH" | grep -iE 'cf-cache-status|cache-control'
|
||
curl -sI "$HOST/cdn/s/$STICKER_ID?v=$HASH" | grep -i cf-cache-status # HIT
|
||
|
||
# 私有 API
|
||
curl -sI "$HOST/api/v1/auth/me" -H "Cookie: wa_session=..." | grep -iE 'cache-control|cf-cache-status'
|
||
# 期望: private, no-store;DYNAMIC 或 BYPASS
|
||
|
||
# 压缩
|
||
curl -sI -H 'Accept-Encoding: br' "$HOST/app" | grep -i content-encoding
|
||
|
||
# 私有 / 未审表情不得公开
|
||
curl -sI "$HOST/cdn/s/$PRIVATE_ID" # 404
|
||
```
|
||
|
||
Dashboard → Caching → Cache Analytics 可看 HIT 率。
|
||
|
||
---
|
||
|
||
## 7. 反代注意
|
||
|
||
- 优先 **CF 橙云 → 源站 443/8787**,中间少一层
|
||
- **多节点负载均衡**:使用仓库内 **`cloudflare-worker/`**(静态 `ORIGINS` + 健康检查 + 轮询),主域名绑 Worker;详见 `cloudflare-worker/README.md`
|
||
- 若 Nginx 仅 TLS 终结再反代 Node:
|
||
- 转发 `Host`、`X-Forwarded-Proto`、`CF-Connecting-IP`
|
||
- **不要**强行剥掉 `Accept-Encoding` 导致双重压缩混乱
|
||
- 源站健康检查:`GET /health`(进程)或 `GET /health/ready`(含 Redis,推荐给 LB)
|
||
- 应用侧 `PUBLIC_BASE_URL` 始终为主域名;源站 IP 不必暴露在后台 UI
|
||
---
|
||
|
||
## 8. 安全边界
|
||
|
||
- `/cdn/s/*` **仅** `visibility=public` 且 `review_status=approved` 且 `enabled`
|
||
- 私有 / pending / rejected → 统一 404
|
||
- 用户 JSON、admin、try-chat、OAuth callback **永不**边缘共享缓存
|
||
- 广场列表含 `inLibrary` 个性化,**不**做公共 CDN 缓存(升级路径:拆无个性化的 public catalog API)
|
||
|
||
---
|
||
|
||
## 9. 可选升级路径(未实现)
|
||
|
||
1. 公开表情同步 **R2** + 自定义域,源站只写 meta
|
||
2. 拆 `GET /api/v1/public/square/*` 无 `inLibrary` 的目录接口短缓存
|
||
3. 内联 CSS/JS 拆出带 content-hash 的静态文件
|
||
4. Cache Reserve + Image Resizing
|
||
|
||
详见主 README / 本文件与 `docs/docker.md` 交叉引用。
|