# 快取分層

本頁說明同一張圖片在瀏覽器、Cloudflare Worker、Nginx 與本地快取檔四層各自的快取 key、有效期與失效方式。

## 四層總覽

| 層 | 快取 key | 有效期 | 設定來源 |
|---|---|---|---|
| 瀏覽器 | 完整 URL | 7 天（`Cache-Control: public, max-age=604800` 與 `Expires`） | `GetFromPath` 標頭；Nginx 另加同值標頭 |
| Cloudflare Worker | 來源 URL ＋ 完整 query string | 7 天（`max-age=604800`） | `worker/index.js` |
| Nginx | `proxy_cache` 預設 key（含 query） | 200／301／302／304 為 7 天，其他狀態 1 分鐘；30 天未存取即淘汰；上限 2 GB | `config/nginx/nodes.conf` |
| 本地快取檔 | 參數組成的檔名（見下節） | 無期限，不會自動清除 | `GetFileCachePath()` |

## 本地快取檔命名

快取檔位於 `storage/image/cache/`，目錄結構對應 `upload/`，檔名去掉原副檔名後串接參數：

| 參數組合 | 檔名格式 |
|---|---|
| 有 `s` | `{名}_{s}_{q}_{b}_{B}.{t}` |
| `w` 與 `h` | `{名}_{w}_{h}_{q}_{b}_{B}.{t}` |
| 只有 `w` | `{名}_{w}_auto_{q}_{b}_{B}.{t}` |
| 只有 `h` | `{名}_auto_{h}_{q}_{b}_{B}.{t}` |
| 皆無 | `{名}_auto_auto_{q}_{b}_{B}.{t}` |

各段使用請求中的**原始字串**，未正規化：`?s=480&t=avif` 產生 `{名}_480___1.avif`（`q`、`b` 為空字串，`B` 預設 `1`），而 `?s=480&t=avif&q=50` 雖然輸出品質相同，卻會產生另一個快取檔。

## 命中順序

```mermaid
graph LR
    Req[請求] --> B{瀏覽器有效？}
    B -- 否 --> W{Worker 命中？}
    W -- 否 --> N{Nginx 命中？}
    N -- 否 --> L{本地快取檔存在？}
    L -- 否 --> V[libvips 轉檔並寫入快取檔]
```

未經 Worker 的請求從瀏覽器直接到 Nginx。`.pdf`、`.svg` 與 `o=1` 不寫本地快取檔，只由前三層快取。

## 失效與清除

程式沒有任何快取清除機制。刪除原檔只會把 `upload/` 內的檔案移入回收桶，`cache/` 內的轉檔結果仍在，且 `GetFromPath` 先查快取檔、後讀原檔，因此**已刪除圖片的既有尺寸版本仍會持續回傳**。需要立即下架時，須手動刪除 `storage/image/cache/` 對應檔案，並清除 Nginx `/var/cache/nginx/images` 與 Cloudflare 快取。
