> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Cloudflare

Note này tập trung vào việc đặt Cloudflare trước AEM để cache response API (JSON từ Sling Model Exporter, Content Services `.model.json`, GraphQL persisted query). Mục tiêu: giảm tải Publish, giảm latency, nhưng vẫn invalidate đúng khi content đổi. Đây là chủ đề khó vì API cache sai sẽ trả data cũ cho hàng loạt user.

## Vị trí Cloudflare trong stack

```mermaid
flowchart TD
  U[Browser / Mobile app] --> CF[Cloudflare Edge]
  CF --> TC[Tiered Cache / Upper tier]
  TC --> D[Dispatcher Apache]
  D --> P[AEM Publish]
  P --> REPO[(Oak repository)]
```

Cloudflare thay thế hoặc bổ sung cho CDN cũ. Với API, Cloudflare cache tại edge nên Dispatcher chỉ nhận request khi MISS. Khác với HTML, API thường bị cache nhầm do query string và header personalization.

## 2026: Cache Rules thay Page Rules

Từ 2026 Cloudflare đã hoàn tất việc loại bỏ Page Rules cũ. Toàn bộ logic cache chuyển sang Cache Rules (engine Ruleset, biểu thức theo field request). Không dùng Page Rules cho dự án mới.

| Tinh nang | Page Rules cu    | Cache Rules 2026                             |
| --------- | ---------------- | -------------------------------------------- |
| Match     | Theo URL pattern | Bieu thuc field: path, header, cookie, query |
| Cache key | Co dinh          | Custom cache key tuy bien                    |
| Edge TTL  | Mot gia tri      | Theo status code, theo bieu thuc             |
| So luong  | Gioi han it      | Nhieu rule co thu tu uu tien                 |

## Cache Rule mẫu cho endpoint API

Cấu hình trong dashboard Cache > Cache Rules. Biểu thức ví dụ match các API JSON của AEM:

```
(http.request.uri.path matches "^/content/.*\\.model\\.json$")
or (http.request.uri.path matches "^/api/.*")
or (starts_with(http.request.uri.path, "/graphql/execute.json"))
```

Action trong rule:

| Setting           | Gia tri khuyen nghi                               | Ly do                               |
| ----------------- | ------------------------------------------------- | ----------------------------------- |
| Cache eligibility | Eligible for cache                                | Bat cache cho response API          |
| Edge TTL          | Override origin, hoac dung `Cache-Control` tu AEM | Kiem soat TTL tap trung             |
| Browser TTL       | Thap hoac Respect origin                          | Tranh browser giu ban cu qua lau    |
| Cache key         | Tuy bien (xem duoi)                               | Tranh phan manh cache do query thua |
| Serve stale       | Bat `stale-while-revalidate`                      | Giam tac dong khi origin cham       |

## Custom cache key cho API

Vấn đề lớn nhất của API cache là cache key. Mặc định Cloudflare đưa toàn bộ query string vào key, làm cache phân mảnh (cache fragmentation) hoặc tệ hơn là cache nhầm theo tracking param.

Nguyên tắc:

- Loại bỏ các query param không ảnh hưởng nội dung: `utm_*`, `gclid`, `fbclid`.
- Chỉ giữ param thực sự đổi response: `page`, `lang`, `category`.
- Vary theo header chỉ khi thật cần, ví dụ `Accept-Language`. Vary bừa làm hit rate tụt.

```
SAI: cache key gom toan bo query string
  /api/products?category=shoes&utm_source=fb&gclid=abc
  -> moi lan tracking param doi tao mot cache entry moi, hit rate thap

DUNG: cache key chi giu param noi dung
  /api/products?category=shoes
  -> tat ca traffic cung category dung chung mot cache entry
```

Cấu hình Custom Cache Key trong Cache Rule: chọn Query String > Include chỉ các param cần, hoặc Ignore query string nếu endpoint không phụ thuộc query.

## Invalidation: phần khó nhất

API cache vô dụng nếu không purge đúng khi content publish. Có ba cấp purge trên Cloudflare.

| Cach purge         | Pham vi               | Dung khi                             |
| ------------------ | --------------------- | ------------------------------------ |
| Purge by URL       | Mot URL cu the        | Biet chinh xac endpoint doi          |
| Purge by Cache-Tag | Nhom URL gan cung tag | Mot content node anh huong nhieu API |
| Purge by Prefix    | Theo path prefix      | Doi ca mot section                   |
| Purge Everything   | Toan bo               | Han che, gay cache miss hang loat    |

### Cache-Tag từ AEM cho surrogate purge

Cách bền vững nhất: AEM (hoặc Dispatcher) gắn header `Cache-Tag` vào response API, Cloudflare lưu tag, khi content đổi thì purge theo tag thay vì liệt kê từng URL.

```
Response tu AEM:
  Cache-Tag: product-123, category-shoes, site-en
```

Khi node `product-123` được activate, gọi Cloudflare API purge theo tag:

```bash
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 '{"tags":["product-123"]}'
```

Tích hợp với AEM: viết một replication event handler hoặc Sling Job lắng nghe activate/deactivate, ánh xạ path sang tag, rồi gọi API purge. Đặt token Cloudflare trong OSGi config được mã hóa, không hardcode.

:::tip
Purge by tag cần plan Enterprise. Nếu chỉ có plan thấp hơn thì dùng Purge by URL hoặc Purge by Prefix. Thiết kế hàm map path -> danh sách URL cần purge ngay từ đầu để dễ đổi chiến lược.
:::

## Không cache API personalized

API trả nội dung theo user (giỏ hàng, profile, giá sau đăng nhập) không được cache ở edge.

```
SAI: cache /api/cart cho moi user
  -> user A thay gio hang cua user B

DUNG: bypass cache cho endpoint personalized
  Tao Cache Rule rieng dat Bypass cache khi co cookie session
  (http.request.uri.path eq "/api/cart") -> Bypass cache
  hoac khi ton tai cookie: http.cookie contains "login-token"
```

Nguyên tắc: cache content public, bypass content phụ thuộc danh tính. Nếu cần cache theo segment, dùng cache key vary theo một header segment đã chuẩn hóa, không vary theo cookie raw.

## Tiered Cache và Cache Reserve

- Tiered Cache: gom request từ nhiều edge về một upper tier trước khi tới origin. Giảm số lần gọi Dispatcher/Publish khi cache cold, tăng hit rate toàn cục. Bật cho API có traffic phân tán địa lý.
- Cache Reserve: lưu object lâu hơn trên R2 storage. Phù hợp API ít đổi nhưng tốn chi phí compute khi origin sinh ra. Cân nhắc chi phí lưu trữ.

## stale-while-revalidate và serve stale

Cho phép edge trả bản cũ trong khi background fetch bản mới, hoặc trả stale khi origin lỗi. Giảm latency và che downtime ngắn của Publish.

```
Cache-Control: public, max-age=60, stale-while-revalidate=300, stale-if-error=86400
```

Diễn giải:

- `max-age=60`: fresh trong 60s.
- `stale-while-revalidate=300`: trong 300s sau khi hết hạn, trả bản cũ ngay và revalidate nền.
- `stale-if-error=86400`: nếu Publish lỗi, trả bản cũ tới 24h thay vì 5xx.

## WAF và rate limit cho API

API là bề mặt tấn công. Cloudflare đặt trước nên tận dụng được:

- WAF managed rules chặn injection, abuse.
- Rate Limiting theo IP hoặc theo path API để chống scraping.
- Bot Management phân biệt bot xấu với crawler hợp lệ.
- Phân biệt rõ endpoint cần auth: đừng để cache che mất logic phân quyền của AEM.

## RCA khi API trả data cũ

Khoanh vùng từ origin ra edge, giống caching layers nhưng thêm tầng Cloudflare.

```bash
# 1. Lay truc tiep tu Publish, bo qua Dispatcher va Cloudflare
curl -H "Host: www.example.com" "http://PUBLISH_HOST:4503/content/site/page.model.json"

# 2. Qua Dispatcher, bo qua Cloudflare
curl -H "Host: www.example.com" "http://DISPATCHER_HOST/content/site/page.model.json"

# 3. Qua Cloudflare, xem cache status
curl -I "https://www.example.com/content/site/page.model.json"
```

Đọc header Cloudflare:

| Header                     | Y nghia                             |
| -------------------------- | ----------------------------------- |
| `cf-cache-status: HIT`     | Tra tu edge cache                   |
| `cf-cache-status: MISS`    | Khong co trong cache, da goi origin |
| `cf-cache-status: EXPIRED` | Het han, da revalidate              |
| `cf-cache-status: BYPASS`  | Rule bypass hoac origin cam cache   |
| `cf-cache-status: DYNAMIC` | Khong eligible, khong duoc cache    |
| `Age`                      | So giay object nam trong cache      |

Diễn giải nhanh:

- Bước 1 đã cũ: lỗi replication/Publish, không phải Cloudflare.
- Bước 1 đúng, bước 2 cũ: Dispatcher giữ bản cũ.
- Bước 2 đúng, bước 3 cũ kèm `HIT`: Cloudflare giữ bản cũ, purge theo tag/URL.
- `cf-cache-status: DYNAMIC` mà muốn cache: Cache Rule chưa match hoặc origin trả `Cache-Control: no-store`.

## Kỹ thuật sinh tồn

- Không bật cache cho mọi `.json` một cách mù quáng. Liệt kê endpoint public an toàn để cache, còn lại để DYNAMIC.
- Luôn thiết kế cơ chế purge tự động theo replication trước khi bật cache API trên production.
- Đo hit rate sau khi đổi cache key. Hit rate thấp thường do query param thừa lọt vào key.
- Tránh Purge Everything trên production giờ cao điểm: cache miss đồng loạt dồn xuống Publish.
- Test bằng `cf-cache-status` và `Age` trong mọi RCA, đính kèm output ba tầng làm bằng chứng.

## Liên quan

- Tổng quan các lớp cache AEM: note Caching Layers.
- Flush Dispatcher: note Dispatcher Cache & Flush.
