Files

194 lines
7.4 KiB
Markdown
Raw Permalink 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.
# 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 cacheEdge 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 cacheEdge 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. 部署后刷新缓存
| 变更 | 做法 |
|------|------|
| 新版本 HTMLapp/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-storeDYNAMIC 或 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` 交叉引用。