Traefik 完整教學 2026:Docker Labels 自動路由與自動 HTTPS

Traefik 是雲原生圈最主流的反向代理(GitHub 65,350 星、MIT、Go 撰寫、最新版 v3.7.16)。它最大的差別是「設定不用重啟」:只要在 docker-compose 加上幾行 labels,Traefik 監聽 Docker 事件就自動生成路由,再配 ACME 自動申請與續期 Let's Encrypt 憑證。本文從 EntryPoints/Router/Service/Middleware 四大概念講起,給你可直接複製的 docker-compose、middleware 實戰、dashboard 保護、8 個實務踩雷,最後用決策表比較 Traefik 與 Caddy 該選誰。

  • Dennis
  • 12 分鐘閱讀
Traefik 完整教學 2026:Docker Labels 自動路由與自動 HTTPS

Traefik 是一套用 Go 寫的雲原生反向代理(GitHub 65,350 星、MIT 授權、最新 v3.7.16)。它跟 Nginx 最大的差別是「路由寫在容器標籤上」:你改 docker-compose 加幾行 labels,Traefik 監聽 Docker 事件就自動生成路由並申請 HTTPS 憑證,全程不用重啟、不用 reload。

如果你的 VPS 上已經跑了五六個容器——密碼管理器、監控、書籤、記帳、AI 聊天介面——那你遲早會遇到同一個問題:每個服務一個連接埠,記不住、防火牆開一堆、憑證還要自己續。Traefik 就是專門解決這件事的,而且它的解法比多數人想的更徹底。

這篇從概念、可複製的設定、middleware 實戰,一路講到 8 個真實踩雷與「Traefik 還是 Caddy」的選擇題。

一表看懂:Traefik 是什麼、跟別人有什麼不同

項目內容
定位Cloud Native Application Proxy(雲原生應用代理)
GitHubtraefik/traefik,65,350 星、6,267 forks、961 open issues(2026-10-24 API 查詢)
授權MIT
語言Go
首版2015-09-13
最新穩定版v3.7.16(2026-10-20 發布,同時維護 v2.11.60)
官方文件https://doc.traefik.io/traefik/
設定來源Docker、Swarm、Kubernetes(Ingress/CRD/Gateway API)、Nomad、Consul、etcd、Redis、ZooKeeper、File、HTTP
憑證內建 ACME(Let’s Encrypt/ZeroSSL、支援 EAB 與 DNS-01 萬用字元)
商業版Traefik Hub(API 管理)、Traefik Enterprise;開源版本身功能完整

它跟 Nginx、Caddy 的根本差異

Nginx 的世界是「設定檔 + reload」:你寫 server {} block,改完 nginx -s reload。Traefik 的世界是「服務自己宣告自己」:容器在標籤裡寫「我是 app.example.com,走 3000 埠」,Traefik 從 Docker API 看到這個容器上線,路由就長出來了。

面向TraefikNginxCaddy
靜態網站可以,但不是強項極強極強
容器自動發現✅ 原生(監聽 Docker 事件)❌ 要自己寫 upstream 或改設定 reload⚠️ 有 docker-proxy 外掛,非內建
自動 HTTPS✅ 內建 ACME❌ 要配 certbot✅ 內建(更省事)
設定心智負擔高(概念多:entryPoint/router/service/middleware)中低(Caddyfile 五行搞定)
動態設定重載✅ 不用重啟⚠️ reload⚠️ 自動 reload 設定檔
進階流量控制✅ middleware 鏈、circuit breaker、retry、rate limit⚠️ 需模組或商業版⚠️ 較少
適合場景多容器、多網域、需要集中治理傳統單體、極致效能單機自架、想少寫設定

一句話:服務數量少、設定想簡單 → Caddy;服務十幾個、要集中管路由與憑證 → Traefik。(Caddy 的完整教學可看文末延伸閱讀。)

核心概念:四個名詞擋住 90% 新手

Traefik 的學習曲線不是語法難,是概念不熟。先把這組關係背起來,後面全部豁然開朗:

名詞角色類比
EntryPoint對外開的門(:80、:443)大樓的門
Router判斷「這個請求該去哪」的規則(Host(...)、PathPrefix(...))門口的接待員
Service真正把流量送到哪個後端與連接埠接待員手上那份名單
Middleware在 Router 與 Service 之間插入的處理(轉址、驗證、壓縮、限流)走道上的關卡
ProviderRouter/Service 這些設定的來源(Docker、File、K8s…)名單是誰給的

而且 Traefik 的設定分成兩層,這是新手最容易混淆的地方:

層級內容需要重啟嗎
靜態設定(Static)entryPoints、providers、certificatesResolvers、api、log、metrics要(因為是啟動參數)
動態設定(Dynamic)routers、services、middlewares不用,Provider 一更新就生效

所以:憑證解析器、入口點放在靜態設定(traefik.yml 或 CLI 參數);路由與 middleware 放在動態設定(Docker labels 或 dynamic file)。

實戰一:最小可用的 docker-compose

以下直接在 VPS 上跑得起來。先建目錄與兩個檔案:

mkdir -p ~/traefik && cd ~/traefik
touch acme.json && chmod 600 acme.json
# ~/traefik/traefik.yml(靜態設定)
api:
  dashboard: true          # 啟用 dashboard,但「不要」開 insecure
  insecure: false

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false      # 只有標了 traefik.enable=true 的容器才曝露
    network: proxy               # 預設用這個 network 連後端
  file:
    directory: /etc/traefik/dynamic
    watch: true

certificatesResolvers:
  le:
    acme:
      email: your-email@example.com
      storage: /acme.json
      httpChallenge:
        entryPoint: web
# ~/traefik/docker-compose.yml
services:
  traefik:
    image: traefik:v3.7
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./dynamic:/etc/traefik/dynamic:ro
      - ./acme.json:/acme.json
    networks:
      - proxy
    labels:
      - "traefik.enable=true"

      # Dashboard router(走 api@internal 服務)
      - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.tls.certresolver=le"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=dash-auth@docker"

      # Dashboard 的基本驗證(htpasswd 產生,compose 內 $ 要寫成 $$)
      - "traefik.http.middlewares.dash-auth.basicauth.users=admin:$$apr1$$xxxxxxxx$$yyyyyyyyyyyyyyyyyyyyyyy"

networks:
  proxy:
    name: proxy

啟動:docker compose up -d,看 log 確認 Configuration loaded 且沒有 ACME 錯誤。

幾個關鍵決定,值得特別說明:

  • exposedByDefault: false — 官方預設是 true(所有容器自動曝露,規則用 Host({{ normalize .Name }}))。自架環境強烈建議關掉,不然你哪天開了一個內網測試容器,它可能就默默對外了。
  • Docker socket 掛 :ro — 唯讀能減少風險,但這不等於安全邊界(有 socket 就能控制 Docker,等同 root)。講究的話請加 socket-proxy(見第八節)。
  • acme.json 用單檔 bind mount 而不是整個目錄 — 這樣權限好控。

實戰二:用 labels 曝露一個服務

假設你已經有 Vaultwarden 或 Karakeep 在跑,把它的 compose 改成下面這樣(重點是 networks 與 labels):

services:
  app:
    image: some/app:latest
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      # 1) Router:哪個網域、哪個入口點
      - "traefik.http.routers.app.rule=Host(`app.example.com`)"
      - "traefik.http.routers.app.entrypoints=websecure"
      - "traefik.http.routers.app.tls.certresolver=le"
      # 2) Middleware:HTTP 轉 HTTPS、加上安全標頭
      - "traefik.http.routers.app.middlewares=redirect-https@file,sec-headers@file"
      # 3) Service:後端容器內部連接埠(容器 expose 多埠時必寫)
      - "traefik.http.services.app.loadbalancer.server.port=3000"
      # 4) 若容器同時接多個 network,指定 Traefik 走哪一個
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

把它拆成四塊記就不會亂:Router(規則/入口點/TLS/middleware)→ Service(後端埠)→ 網路(走哪張網)→ 開關(traefik.enable)。

驗證方式:開 dashboard 的 HTTP Routers 區,看得到這個 router 就代表設定被吃進去了。看不到的話通常是三件事:沒標 traefik.enable=true、容器不在 proxy network、或 label 拼錯(Traefik 不會報錯,只會忽略)。

實戰三:自動 HTTPS 三種挑戰怎麼選

Traefik 的 ACME 靠 Lego 實作,支援的 DNS provider 清單也在那邊。三種驗證方式差異很大:

挑戰需求萬用字元適用
HTTP-01(httpChallenge)80 埠必須對外可達 Let’s Encrypt❌單機、80 埠沒被佔用
TLS-ALPN-01(tlsChallenge)443 埠必須對外可達❌80 埠被 ISP 封鎖(例如家用線路)
DNS-01(dnsChallenge)需要 DNS provider API token✅要 *.example.com、或完全不想開 80 埠

萬用字元憑證只能走 DNS-01,這是 ACME 協定層的限制。DNS-01 的設定長這樣:

certificatesResolvers:
  le:
    acme:
      email: your-email@example.com
      storage: /acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

對應的環境變數依 provider 而定(Cloudflare 是 CF_DNS_API_TOKEN),要一併傳給 Traefik 容器。若你的內網擋外部 DNS 查詢,可加 propagation.delayBeforeChecks 延後 TXT 檢查。

其他幾個憑證相關的預設值,知道一下比較不會被嚇到:

參數預設說明
acme.keyTypeRSA4096想用小一點的憑證可改 EC256
acme.certificatesDuration2160(小時=90 天)只用來推算續期時間
續期時機到期前 30 天自動追蹤到期日
acme.storageacme.json必填

官方也提醒一件事:Traefik 不會檢查憑證是否還在使用,已不用的網域仍可能被續期。所以網域下線後記得清 acme.json,不然會累積。

實戰四:Middleware 實戰四件套

Middleware 建議集中寫在 dynamic file(dynamic/middlewares.yml),這樣多個容器可以共用,不必在每個 compose 重複貼。

# ~/traefik/dynamic/middlewares.yml
http:
  middlewares:
    # 1) HTTP → HTTPS 永久轉址
    redirect-https:
      redirectScheme:
        scheme: https
        permanent: true

    # 2) 安全標頭(HSTS、nosniff、frame deny)
    sec-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        contentTypeNosniff: true
        frameDeny: true
        browserXssFilter: true

    # 3) gzip/zstd 壓縮
    gzip:
      compress: true

    # 4) 限流:平均 100 req/s、突發 50
    rate-limit:
      rateLimit:
        average: 100
        burst: 50

套用到 router 時用逗號串接,順序就是執行順序(先轉址、再壓縮、最後限流):

- "traefik.http.routers.app.middlewares=redirect-https@file,gzip@file,rate-limit@file"

這裡有個 Traefik 特有語法要注意:middleware 與 service 的引用都要帶 provider 後綴——@file、@docker、@internal。少了 @file 就會出現「middleware not found」,是新手最高頻的錯誤之一。

RedirectScheme 的一個隱藏陷阱

官方文件特別註明:如果 Traefik 前面還有另一層反向代理(例如 Cloudflare、或另一台 Nginx),那個上一跳必須被設為 trusted。否則 Traefik 會清掉它帶來的 X-Forwarded-* 標頭,而 redirectScheme 正是靠這些標頭判斷原始 scheme——結果就是轉址失效或無限迴圈。解法是在 entryPoint 設定 forwardedHeaders.trustedIPs。

實戰五:把 dashboard 安全地開出來

官方文件講得很清楚,dashboard 與 API 必須同時保護,做法是「建一個 router 指向 api@internal,掛上驗證 middleware」。規則必須涵蓋 /api 與 /dashboard 兩個前綴,最簡單的寫法是直接用 Host 規則:

- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=dash-auth@docker"

想更嚴格可以改成 Host(...) && (PathPrefix(\/api`) || PathPrefix(`/dashboard`)),或再加一層 ipAllowList` 限定來源 IP。

千萬不要為了方便把 api.insecure: true 開在生產環境——那等於把 dashboard 與 API 無驗證掛在 8080 埠上,官方文件直接用粗體警告這件事。

密碼用 htpasswd -nbB admin '你的密碼' 產生,貼進 label 時 compose 內的 $ 必須寫成 $$(compose 會做變數展開,這是第二高頻的踩雷)。

8 個實務踩雷(按踩到機率排序)

#症狀原因解法
1502 Bad Gateway容器不在 Traefik 所在 network,或連錯埠讓容器與 Traefik 同接 proxy network;必要時標 traefik.docker.network=proxy
2502 且埠明顯錯容器 expose 多個埠,Traefik 猜錯明確寫 traefik.http.services.<name>.loadbalancer.server.port=3000
3Router 完全沒出現沒標 traefik.enable=true(在 exposedByDefault: false 下)補 label;或檢查 label 前綴有沒有打錯(打錯不會報錯,只會靜默忽略)
4middleware "xxx" does not exist引用時漏了 @file / @docker 後綴一律寫成 xxx@file
5ACME 憑證要不到acme.json 權限太開,Traefik 拒絕載入chmod 600 acme.json(經典錯誤訊息:permissions ... for /acme.json are too open)
6HTTP 沒自動轉 HTTPS忘了掛 redirectScheme,或前面還有代理導致 X-Forwarded-* 被清加 middleware;多層代理時設 forwardedHeaders.trustedIPs
7想簽 *.example.com 一直失敗用了 HTTP-01/TLS-ALPN-01萬用字元只能走 DNS-01
8連線數一多就掛、日誌爆量Docker socket 直通 + 沒有限流/沒開 access log 輪替改 socket-proxy、加 rateLimit、設定 accessLog 輪替

第 5 點值得多說一句:這是從 v2 時代就存在的長期行為(社群 issue 從 2017 年一路到 2020 年都有人在問),它就是設計上的故意——避免憑證私鑰被同機其他使用者讀走。所以 touch acme.json && chmod 600 是每個 Traefik 教學都該寫、卻常常漏寫的一行。

進階:Docker socket 的安全替代方案

把 /var/run/docker.sock 直接掛進容器,等於給 Traefik(以及任何能打進 Traefik 漏洞的人)root 等級的宿主控制權。標準做法是中間夾一層 socket proxy,只開放唯讀端點:

services:
  socket-proxy:
    image: tecnativa/docker-socket-proxy
    restart: unless-stopped
    environment:
      CONTAINERS: 1     # 只開容器讀取
      SERVICES: 1
      TASKS: 1
      NETWORKS: 1
      POST: 0           # 關掉所有寫入
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy

  traefik:
    # ...
    environment:
      - DOCKER_HOST=tcp://socket-proxy:2375
    # 移除原本的 docker.sock 掛載

代價是多一個容器、多一個故障點,但這是把「Traefik 被攻破」的影響範圍從「整台機器」縮小到「只看得到容器清單」的關鍵一步。

該用 Traefik 嗎?三種情境的決策表

你的狀況建議理由
一台 VPS、3 個以下服務、想快點上線CaddyCaddyfile 五行收工,概念少一半
一台 VPS、5 個以上容器、常常加新服務Traefik加服務只改該服務的 compose,不用動代理設定
多網域 + 多子網域 + 要萬用字元憑證TraefikDNS-01 與 certificateResolver 集中管理
未來要搬上 KubernetesTraefik同一套概念直接對應 Ingress/CRD
只跑靜態網站、追求極致靜態效能Nginx 或 CaddyTraefik 不是靜態檔案伺服器的最佳解
團隊沒人想學新概念Caddy學習成本是 Traefik 最大的隱形成本

老實說,Traefik 的設定負擔比你預期的高。它的抽象層很漂亮,但出事時的除錯資訊不算友善——label 拼錯不會報錯、路由優先級要自己推、middleware 順序有意義。如果你只是想讓一個部落格有 HTTPS,用 Caddy 三分鐘就好,不必為了「雲原生」三個字付學費。

但如果你已經有十個容器在跑、每次新增服務都要手動加 Nginx upstream 再 reload,那 Traefik 省下的時間會在第三次新增服務時就回本。

查證說明

  • 星數/forks/issue 數、最新版本:2026-10-24 以 GitHub API(api.github.com/repos/traefik/traefik、/releases)查詢,非網頁快取值。官方文件頁面顯示 65.4k 星、6.3k forks,與 API 一致。
  • 設定語法、預設值、ACME 選項、dashboard 保護方式:全部取自官方文件(quick-start、ACME、Docker provider、API & Dashboard、RedirectScheme、BasicAuth 各頁)。
  • 本文未實測的部分:沒有在本次寫作環境實際起一套 Traefik 跑完 ACME 簽發流程(需要真實網域與 80/443 對外)。所有 YAML 皆逐項對照官方文件語法,但請以你自己的環境實跑結果為準。第 5 點的 acme.json 權限行為引用自專案長期存在的社群 issue(如 #6972),不同版本訊息措辭可能略有差異。

延伸閱讀

資料來源

  • Traefik 官方文件 — Docker Quick Start:https://doc.traefik.io/traefik/getting-started/quick-start/
  • Traefik 官方文件 — Docker Provider:https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/
  • Traefik 官方文件 — ACME Certificate Resolver:https://doc.traefik.io/traefik/https/acme/
  • Traefik 官方文件 — API & Dashboard:https://doc.traefik.io/traefik/reference/install-configuration/api-dashboard/
  • Traefik 官方文件 — RedirectScheme:https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/redirectscheme/
  • Traefik 官方文件 — BasicAuth:https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/basicauth/
  • GitHub API — https://api.github.com/repos/traefik/traefik(查證於 2026-10-24)
  • Lego(ACME 實作與 DNS provider 清單):https://go-acme.github.io/lego/

📬 訂閱 most.tw 電子報

每週精選 AI 工具教學與技術乾貨,直接送到你的信箱。免費、隨時可退訂。

訂閱即表示同意收到 most.tw 電子報,隨時可一鍵退訂。

💬 有問題想討論?加 LINE 聯絡我

歡迎透過 LINE 官方帳號直接留言,我會盡快回覆你的問題。

加入 LINE 好友