Skip to content

Caddy 靜態檔案伺服器

使用 Caddy 取代 Nginx 作為 Kubernetes 上的靜態檔案伺服器,提供目錄瀏覽、分層快取策略與內建 Prometheus metrics。

概述

Caddy 是一款以簡潔設定與自動 HTTPS 聞名的現代 Web 伺服器。在本場景中,Caddy 被用來取代 Nginx 作為靜態檔案伺服器,部署在 Kubernetes 叢集中,提供目錄瀏覽(autoindex)與檔案下載功能。

相較於 Nginx,Caddy 的 Caddyfile 語法更簡潔直觀,且內建 Prometheus metrics 支援,無需額外安裝模組。搭配 zstdgzip 壓縮,能有效降低傳輸頻寬。映像採用 caddy:2.10-alpine,體積輕巧。

核心內容

Caddyfile 設定架構

Caddyfile 分為兩個區塊:全域設定(Global Options)與站台設定。

全域設定: 啟用 Admin API(監聽 0.0.0.0:2019)並開啟內建 Prometheus metrics(含 per-host 指標),讓 Prometheus Exporter 部署模式 可直接抓取 Caddy 自身的運行指標,無需額外部署 Exporter。

站台設定(:80):/srv/files 為檔案根目錄,啟用 zstdgzip 壓縮(Caddy 依據客戶端的 Accept-Encoding 自動選擇最佳壓縮演算法)。

分層快取策略

Caddy 針對不同類型的資源設定了四層快取策略,透過 handle 指令與路徑匹配實現:

目錄瀏覽路徑(30 秒): 僅特定路徑(/bj6-updater/public-updater/resource-viewer/mrp-report/wiwynn)開放 file_server browse 目錄瀏覽功能,快取時間極短以確保使用者能即時看到更新。

大型下載檔案(30 分鐘): 匹配 .zip.tar.gz.exe.AppImage.deb 等壓縮檔與安裝包,設定較長的快取時間以減少重複傳輸。

前端動態資源(30 秒): 匹配 .html.css.js.json.wasm 等前端資源,搭配 must-revalidate 確保客戶端總是驗證資源是否已更新。

其他檔案(5 分鐘): 不屬於以上任何類別的檔案,給予中等快取時間。

所有快取策略均使用 Vary: Accept-Encoding header,確保壓縮與非壓縮版本的快取不會互相干擾。

目錄瀏覽安全性

值得注意的是,目錄瀏覽功能僅限於三個白名單路徑,其餘路徑雖然可以直接下載已知檔名的檔案,但不會列出目錄內容。這是一個重要的安全考量——避免意外暴露不應公開的檔案清單。

Kubernetes 部署細節

Caddy 容器以 sidecar 或獨立 Pod 形式部署,掛載兩個 Volume:

檔案目錄: 來自名為 sftpgo-volume 的 Volume(subPath: upload),以 readOnly 模式掛載至 /srv/files。這表示 Caddy 與 SFTPGo 共享同一份檔案儲存——使用者透過 SFTP 上傳的檔案,可立即透過 Caddy 的 HTTP 介面瀏覽與下載。

設定檔: Caddyfile 透過 ConfigMap 以 subPath 方式掛載至 /etc/caddy/Caddyfile

容器設定 TZ=Asia/Taipei 確保日誌時間戳為台灣時區,資源請求為 CPU 150m / Memory 300Mi。

關鍵要點

  • Caddy 以簡潔的 Caddyfile 取代 Nginx 複雜的設定語法,降低維護門檻
  • 內建 Prometheus metrics 支援,省去額外部署 Exporter 的工作
  • 四層快取策略針對不同資源類型最佳化,兼顧即時性與效能
  • 目錄瀏覽採白名單制,僅特定路徑開放,兼顧易用性與安全性
  • 與 SFTPGo 共享 Volume,形成「SFTP 上傳 → HTTP 下載」的完整檔案分發管線

實際應用

此架構適合企業內部的檔案分發場景:開發團隊透過 SFTP 上傳建置產物(build artifacts)、更新檔、報表等,內網使用者則透過瀏覽器直接下載。Caddy 的快取策略確保頻繁更新的前端資源與較少變動的安裝包各自有合適的快取行為。

部署設定參考

以下為實際部署時使用的完整設定,供日後查詢與複製使用。

環境參數

項目
Container 名稱caddy-server
Imagecaddy:2.10-alpine
Image Pull PolicyAlways
時區Asia/Taipei
對外 Port80
Memory Request300Mi
CPU Request150m

完整 Caddyfile

caddyfile
{
  admin 0.0.0.0:2019
  servers {
    metrics {
      per_host
    }
  }
}

:80 {
  root * /srv/files
  encode zstd gzip

  # --- 目錄瀏覽(autoindex):短快取,30s 內可看到更新 ---
  @browsePaths {
    path /bj6-updater* /public-updater* /resource-viewer/mrp-report/wiwynn*
  }
  handle @browsePaths {
    header {
      Cache-Control "public, max-age=30, must-revalidate"
      Vary "Accept-Encoding"
    }
    file_server browse
  }

  # --- 大型下載檔案:較長快取 ---
  handle /*.(zip|tar|gz|tgz|bz2|xz|7z|rar|exe|AppImage|deb|rpm) {
    header {
      Cache-Control "public, max-age=1800"
      Vary "Accept-Encoding"
    }
    file_server
  }

  # --- 前端動態資源:短快取 + revalidate ---
  handle /*.(html|css|js|json|wasm) {
    header {
      Cache-Control "public, max-age=30, must-revalidate"
      Vary "Accept-Encoding"
    }
    file_server
  }

  # --- 其他檔案:中等快取 ---
  handle {
    header {
      Cache-Control "public, max-age=300, must-revalidate"
      Vary "Accept-Encoding"
    }
    file_server
  }
}

Kubernetes YAML(Container 片段)

yaml
- name: caddy-server
  image: caddy:2.10-alpine
  imagePullPolicy: Always
  env:
    - name: TZ
      value: Asia/Taipei
  ports:
    - containerPort: 80
  volumeMounts:
    - mountPath: /srv/files
      name: sftpgo-volume
      subPath: upload
      readOnly: true
    - name: caddy-config-volume
      mountPath: /etc/caddy/Caddyfile
      subPath: Caddyfile
  resources:
    requests:
      memory: "300Mi"
      cpu: "150m"

Volume 掛載摘要

掛載路徑Volume 名稱說明
/srv/filessftpgo-volume(subPath: upload,readOnly)檔案目錄
/etc/caddy/Caddyfilecaddy-config-volume(subPath: Caddyfile設定檔覆蓋

相關概念

來源