Caddy2 支援 WebP 和 AVIF 03-24

語言: CN / TW / HK

目前對於 WebP 和 AVIF 格式支援大致有兩種方案, 一種是動態轉換, 另一種是靜態轉換.

  • 動態轉換: 即在流量的代理層進行處理, 實現對使用者的透明化, 使用者無需進行任何更改, 由負載均衡器或者中介軟體進行動態轉換處理.
  • 靜態轉換: 通過工具預先轉換好, 然後通過請求匹配分析來選擇返回的圖片格式.

二、動態轉換

懶得放程式碼了, 自己搓到一半放棄了.

對於 WebP 格式來說, 在 Caddy2 上動態轉換比較簡單, 基本上就是新增一個外掛即可; 例如 caddy-webp 這個外掛. 有關於 Caddy 的外掛開發可以參考官方的 Extending Caddy 文件.

動態轉換的核心思想是在接收到請求後, 判斷請求中的 Accept 頭, 如果包含 image/webp 則說明瀏覽器可以識別 WebP 格式圖片(AVIF 同理); 此時可以將請求先轉發給後續的 HTTP 處理邏輯, 待返回響應後將其暫時 Cache 住, 然後執行轉換邏輯, 改變其格式後重新設定 Content-Type 頭然後把新的格式資料返回給前端.

針對這種操作可以在負載均衡器層(例如 Caddy2 Plugin)完成, 也可以通過單獨的中介軟體完成; 但無論哪種其涉及到一些缺陷(可優化):

  • 每次響應需要先完成 load 到記憶體進行轉換(記憶體消耗大)
  • 轉換過程會浪費 CPU 資源(CPU 一樣消耗很大)
  • 轉換過程可能導致請求失敗

針對這些情況, 個人認為在生產環境資源充足的情況下完全可以解決, 核心思想還是一個: 快取為王; 想辦法在第一次轉換後將其儲存到快取位置, 避免每次都轉換; 如果資源足夠甚至可以考慮記憶體級的快取方案配合 LRU 等失效策略.

三、靜態轉換

經過一波搓程式碼從入門到放棄, 最終我選擇了目前我這個小破站能承擔得起的方案; 即先在本地執行轉換然後直接扔到伺服器上; 由於網站本身就是靜態站點, 所以可以在 Caddyfile 中基於動態轉換的套路處理請求頭返回不同檔案.

本地轉換目前採用 optimizt 這個工具完成:

# 安裝
npm i -g optimizt

# 轉換
optimizt --force --webp --avif public/img

這樣在我的圖片目錄下就會生成同名的 WebP 和 AVIF 格式圖片

最後只需要在 Caddyfile 中增加以下請求頭匹配的配置即可:

# avif support
@acceptsAVIF {
    header Accept *image/avif*
    path_regexp avif ^(.+)\.(jpg|jpeg|png)$
}
handle @acceptsAVIF {
    @hasAVIF file {re.avif.1}.avif
    rewrite @hasAVIF {re.avif.1}.avif
}

# webp support
@acceptsWebp {
    header Accept *image/webp*
    path_regexp webp ^(.+)\.(jpg|jpeg|png)$
}
handle @acceptsWebp {
    @hasWebp file {re.webp.1}.webp
    rewrite @hasWebp {re.webp.1}.webp
}

四、總結

動態轉換比價適合商業化模式, 在資源支撐足夠的情況下可以延遲隨機、預熱、高效能快取等方式配合起來將圖片透明轉換完成, 對使用者無感且友好. 小破站資源匱乏等情況比較適合靜態轉換完後邏輯匹配規則返回不同物理檔案. 說實話我想弄成高大上的動態轉換寫點程式碼的, 但是弄到一半發現全是問題又沒資源解決, 只能在打打嘴炮了.