Authelia 完整教學 2026:自架 SSO 單點登入與兩步驗證

Authelia 是 Go 寫的開源認證入口(GitHub 29,564 星、Apache-2.0、映像檔 110 MB),用 forward-auth 幫 Caddy、Traefik、Nginx 後面的自架服務加上一道統一的登入與兩步驗證。本文提供可直接複製的 docker-compose 與 configuration.yml、Argon2id 密碼雜湊產生指令,並附上實測驗證結果、五個實際踩到的坑,最後比較 Authelia、Authentik、Keycloak 該選誰。

  • Dennis
  • 10 分鐘閱讀
Authelia 完整教學 2026:自架 SSO 單點登入與兩步驗證

Authelia 是 Go 語言撰寫的開源認證入口(GitHub 29,564 星、Apache-2.0、Docker 映像檔 110 MB),透過 forward-auth 幫 Caddy/Traefik/Nginx 後面的自架服務統一加上登入頁與兩步驗證,不必逐一修改每個服務的帳號系統。

為什麼自架到某個數量之後,一定會想裝它

自架好玩的地方是很自由,麻煩的地方也是自由:每個服務一套帳號。

階段你有哪些服務帳號現況真正的痛點
剛開始(1–3 個)Vaultwarden、Homepage各自一組密碼還好,密碼管理器能處理
長大中(5–10 個)加上 Nextcloud、Paperless、Uptime Kuma、Portainer各自一組,有些還共用誰能看哪個服務?離職/換人怎麼辦?
完整自架(10+)加上內部 API、Dashboard、監控有人沒設密碼,只靠內網內網不是安全邊界,一個外洩就一串

真正麻煩的從來不是「多記幾組密碼」,而是**「這個服務該給誰看」這件事沒有單一控制點**。你想讓家人看 Jellyfin、不給看 Portainer;想讓同事看內部文件、不給碰監控。沒有認證層時,這些規矩只能靠「不要把網址告訴他」來實現——那不叫權限控管,那叫默契。

Authelia 解決的就是這一層:一個登入頁、一份使用者清單、一組政策規則,所有後面的服務共用。

一表看懂 Authelia

項目內容
專案authelia/authelia(GitHub)
最新版本v4.39.28(2026-09-17 發布)
語言/授權Go/Apache-2.0
社群規模29,564 星、1,539 forks(2026-11-05 查詢)
Docker 映像檔110 MB(authelia/authelia:4.39.28)
實測記憶體閒置 117.4 MiB、登入活動中約 221.5 MiB
使用者來源檔案(YAML)、LDAP、AD
第二因素TOTP、WebAuthn、Duo、行動推播
認證協定Forward-auth、OpenID Connect 1.0(OpenID 認證過)
資料存放SQLite/PostgreSQL/MySQL(實測 db 304 KB)
適合誰已經有一組自架服務、想加統一登入與權限的人

它是怎麼運作的:forward-auth 的四步

Authelia 不代理你的流量,它只回答一個問題:「這個請求該不該放行?」流程如下:

步驟誰在做發生什麼事
1瀏覽器請求 wiki.example.com,實際上打到反向代理
2反向代理先問 Authelia /api/authz/forward-auth,並附上 X-Forwarded-Host、X-Forwarded-Uri 等標頭
3Authelia依 access_control 規則判斷:沒登入回 302 導向登入頁;登入但不符政策回 403;通過則回 200 並附 Remote-User、Remote-Groups 等標頭
4反向代理收到 200 才把請求轉給後端服務,並把 Remote-* 標頭一起帶過去

這裡有個關鍵差異值得先記住,後面實測會再看到一次:302 和 403 是完全不同的兩件事。302 是「身分還沒確認,先去登入」;403 是「身分確認了,但這個身分不准進來」。很多教學只講前者,實際上線後最常撞到的是後者。

實測環境與設定

測試環境:Ubuntu 主機、Docker 29.2.1、Docker Compose v5.1.0,網域假設為 most.lab(auth.most.lab 是登入入口,wiki.most.lab、app.most.lab 是被保護的服務)。

先開目錄,把設定檔與資料庫放在同一個 config/:

mkdir -p ~/authelia/config && cd ~/authelia

docker-compose.yml:

services:
  authelia:
    image: authelia/authelia:4.39.28
    container_name: authelia
    volumes:
      - ./config:/config
    ports:
      - "9091:9091"
    environment:
      - TZ=Asia/Taipei
    restart: unless-stopped

config/configuration.yml(這份可以直接跑,記得換掉三個 secret):

theme: 'dark'
server:
  address: 'tcp://:9091'
  endpoints:
    authz:
      forward-auth:
        implementation: 'ForwardAuth'
log:
  level: 'debug'
identity_validation:
  reset_password:
    jwt_secret: '換成你的隨機字串_0123456789abcdef'
totp:
  issuer: 'most.tw'
  period: 30
authentication_backend:
  file:
    path: '/config/users.yml'
    password:
      algorithm: 'argon2'
      argon2:
        variant: 'argon2id'
        iterations: 3
        memory: 65536
        parallelism: 4
        key_length: 32
        salt_length: 16
access_control:
  default_policy: 'deny'
  rules:
    - domain: 'auth.most.lab'
      policy: 'bypass'
    - domain: 'wiki.most.lab'
      policy: 'one_factor'
    - domain: '*.most.lab'
      subject: 'group:admins'
      policy: 'two_factor'
session:
  name: 'authelia_session'
  secret: '換成你的隨機字串_0123456789abcdef'
  expiration: '1h'
  inactivity: '5m'
  cookies:
    - domain: 'most.lab'
      authelia_url: 'https://auth.most.lab'
      default_redirection_url: 'https://wiki.most.lab'
regulation:
  max_retries: 3
  find_time: '2m'
  ban_time: '5m'
storage:
  encryption_key: '換成你的隨機字串_0123456789abcdefgh'
  local:
    path: '/config/db.sqlite3'
notifier:
  filesystem:
    filename: '/config/notification.txt'

config/users.yml 的密碼不能寫明碼,要用 Argon2id 雜湊。產生的指令在映像檔裡就有,不必另外裝工具:

docker run --rm authelia/authelia:4.39.28 \
  authelia crypto hash generate argon2 --password '你的密碼' --variant argon2id

實測輸出長這樣($ 是雜湊的一部分,貼進 YAML 時整串用雙引號包起來):

Digest: $argon2id$v=19$m=65536,t=3,p=4$ZizE97qy3o/mfQTmRSdTdw$y/PXYYBi8HlY0nBoAT09FI1PRrB348V2ftpMUB93Yfc
users:
  dennis:
    displayname: "Dennis"
    password: "$argon2id$v=19$m=65536,t=3,p=4$ZizE97qy3o/mfQTmRSdTdw$y/PXYYBi8HlY0nBoAT09FI1PRrB348V2ftpMUB93Yfc"
    email: "dennis@most.lab"
    groups:
      - "admins"

啟動前先驗設定

Authelia 內建驗證器,這一步能省下大量「容器起不來但看不出原因」的時間:

docker run --rm -v ~/authelia/config:/config authelia/authelia:4.39.28 authelia config validate
docker compose config --quiet

通過時會印出 Configuration parsed and loaded successfully without errors.。實測時它立刻抓到一個設定錯誤(下一節的坑一)。

實測結果:每個端點真的回什麼

啟動後逐項打 API,結果如下(皆為本次實測原文):

測試請求實際回應
健康檢查GET /api/health{"status":"OK"}
登入頁GET /200,回傳前端 SPA 外殼
帳密登入POST /api/firstfactor{"status":"OK","data":{"redirect":"https://auth.most.lab"}},並發出 Set-Cookie: authelia_session=...; domain=most.lab; HttpOnly; secure; SameSite=Lax
使用者資訊GET /api/user/info{"status":"OK","data":{"display_name":"Dennis","emails":["d****s@most.lab"],"has_totp":false,...}}
認證等級GET /api/state{"username":"dennis","authentication_level":1,"factor_knowledge":true,...}
未登入時的 forward-authGET /api/authz/forward-auth + X-Forwarded-Host: wiki.most.lab302,Location: https://auth.most.lab/?rd=https%3A%2F%2Fwiki.most.lab%2F&rm=GET
已登入(1FA)+ one_factor 政策同上200,並帶 Remote-User: viewer、Remote-Groups: users、Remote-Name、Remote-Email
已登入但群組不符(two_factor + group:admins)X-Forwarded-Host: app.most.lab403
未列在規則中的網域(default_policy: deny)X-Forwarded-Host: vault.most.lab403

兩個細節值得畫線:

  • 注意 emails 回傳的是 d****s@most.lab——Authelia 把 email 遮蔽了,不是 bug。
  • authentication_level: 1 代表只過第一因素;完成第二因素後會變 2。這個數字就是 forward-auth 判斷「能不能放行」的依據。

接到反向代理:Caddy 的官方寫法

Authelia 官方文件給的 Caddy 設定如下(/api/authz/forward-auth 是 4.39 的現行端點,舊教學裡的 /api/verify 已經換掉):

# Authelia Portal.
auth.example.com {
        reverse_proxy authelia:9091
}

# Protected Endpoint.
nextcloud.example.com {
        forward_auth authelia:9091 {
                uri /api/authz/forward-auth
                copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
        }

        reverse_proxy nextcloud:80
}

copy_headers 那行不能省,否則後端服務拿不到登入者身分,只能做「有登入/沒登入」的粗粒度控制。用 Traefik 的話概念一樣,把 forward-auth 換成 forwardAuth middleware 指向同一個端點;用 Nginx 則走 auth_request。站上兩篇反向代理教學可以直接對照:

五個實測踩到的坑

坑一:default_redirection_url 不能等於 authelia_url

一開始兩個都寫 https://auth.most.lab,config validate 直接擋下來:

session: domain config #1 (domain 'most.lab'): option 'default_redirection_url'
with value 'https://auth.most.lab' is effectively equal to option 'authelia_url'
with value 'https://auth.most.lab' which is not permitted

改成一個真正的受保護頁面(例如 https://wiki.most.lab)就過。這個設計是對的:登入完把你丟回登入頁本身,等於沒有預設目的地。

坑二:規則順序決定一切,第一條命中就結束

一開始把 *.most.lab 的通配規則寫在 wiki.most.lab 前面,結果明明登入了、one_factor 卻一直拿到 302。原因很簡單——Authelia 由上往下比對,wiki.most.lab 先被 *.most.lab 那條接走,套用 two_factor,而當時只有 1FA,所以被導回登入頁。

把 wiki.most.lab 那條往前搬,重新起容器後同一個請求立刻變 200 並帶 Remote-User。寫規則時把具體網域放前面、通配放後面,這一條就能省掉半天。

坑三:cookie domain 對不上時,登入成功但不給你 session

第一次用 http://127.0.0.1:9099 測,帳密明明是對的,回應卻只說認證失敗。翻日誌才看到真正的錯誤:

level=error msg="Failed to get session provider during 1FA attempt"
error="unable to retrieve session cookie domain provider: no configured session
cookie domain matches the url 'http://127.0.0.1:9099/api/firstfactor'"

因為 session.cookies[].domain 設定成 most.lab,請求的 host 卻不屬於這個網域,Authelia 找不到可用的 cookie 網域,就只能拒絕。

這件事有兩個實務意義。第一,本機測試一定要用真實網域,用 curl --resolve auth.example.com:9091:127.0.0.1 或改 /etc/hosts,不要用 IP。第二,錯誤訊息常被前端簡化成「認證失敗」,遇到「密碼明明對卻進不去」時,第一件事是去看 docker logs,不是重設密碼。

坑四:費率限制會把你鎖在門外,而且重開機也沒用

regulation 的 max_retries: 3 不是裝飾。實測連續打 4 次錯誤密碼後,第 5 次換上正確密碼仍然被拒絕,日誌寫得很白:

Unsuccessful 1FA authentication attempt by user 'dennis' and they are banned
until 4:08:03AM on November 6 2026

更關鍵的是:把容器砍掉重建之後,同一個帳號還是進不去——因為封鎖狀態存在 db.sqlite3 裡,跟著 config/ 目錄一起被保留下來。這對正式環境是好事(重啟不能繞過封鎖),對測試環境是陷阱。設定的時候記得:ban_time 太長會讓自己進不去,太短則失去防暴力破解的意義,5 到 15 分鐘是多數自架情境的合理值。

坑五:記憶體不會是「幾 MB」

自架圈很流行「輕量」這個詞,所以實測數字有意義:映像檔 110 MB、閒置記憶體 117.4 MiB、登入活動期間約 221.5 MiB。跟 Beszel 那種 11–15 MB 等級的單一功能小工具不同,Authelia 是一個完整認證入口(入口頁、規則引擎、session 儲存、費率限制),兩百多 MB 是合理代價,但 1 GB 記憶體的 VPS 上要把它算進去。

Authelia、Authentik、Keycloak 怎麼選

三套都成熟,差別在定位與複雜度。以下社群數字為 2026-11-05 以 GitHub API 查詢,功能描述依各自官方文件:

項目AutheliaAuthentikKeycloak
GitHub 星數29,56426,24437,719
語言GoPythonJava
授權Apache-2.0非標準 SPDX(GitHub 判定 NOASSERTION)Apache-2.0
主要定位反向代理前的認證層完整 IdP 平台(含管理介面、流程編輯器)企業級 IdP/IAM
使用檔設定YAML 為主幾乎全在 Web UI幾乎全在 Admin Console
Forward-auth原生、設定簡單支援(Proxy Provider)需額外元件
OIDC Provider支援(已認證)支援支援(最完整)
記憶體實測117–221 MiB未實測未實測
適合誰想用一份 YAML 管好自架服務的人想要圖形化管理、多協定與 SSO 入口的人企業、需要標準化合規與多租戶

選型的實務建議很直接:你只要「幫自架服務加一道門」就選 Authelia;你要「當全公司的身分提供者」就往 Authentik 或 Keycloak 走。 站上另有一篇 Keycloak 的完整介紹可以對照:

已知限制與這次沒做的事

依本站慣例,把「查證過」與「沒查證」分開講清楚:

  • 2FA 綁定流程沒有在 CLI 完成。 以 API 直接呼叫 TOTP 註冊端點時,被要求 elevated session(先回 {"status":"KO","data":{"elevation":true,...}});補做 POST /api/firstfactor/reauthenticate 拿到 {"status":"OK"} 之後,端點仍回 {"status":"KO","data":{"elevation":false,"first_factor":true,...}}。結論是:TOTP 綁定設計給瀏覽器流程,headless 環境下沒有走完。文章裡的 2FA 章節因此沒有實測成功的背書,官方 UI 步驟請以文件為準。
  • 沒有接真實的反向代理。 Caddy/Traefik/Nginx 的片段引自官方文件,本次是用 curl 直接模擬 X-Forwarded-* 標頭驗證 Authelia 端的行為(200/302/403 都是真的),但「代理層 → 後端服務」那一段沒有實際串起來測。
  • 沒有測 LDAP/AD 後端,只測檔案後端。多數人從檔案開始,但要接公司目錄就得另外驗證。
  • 沒有做壓力測試,所以併發下的延遲與記憶體峰值未知。
  • 星數、版本、映像檔大小皆為 2026-11-05 查詢值,會變動;功能比較以官方文件描述為準,未逐項實測 Authentik 與 Keycloak。

誰適合、誰還不需要

適合現在裝的人:已經有 5 個以上自架服務、服務分散在不同網域或埠、家裡有其他使用者(家人、同事)需要分權限、而且你願意維護一份 YAML。

可以先不用的人:只有兩三個服務(服務自己的帳號系統還撐得住)、全部只在自己電腦的 VPN 內使用(Tailscale 這類 mesh VPN 本身已是一層邊界,可先參考 Tailscale 完整教學 2026)、或者你根本不打算把任何東西曝露到公網。

站內延伸閱讀

原始來源

(本文非商業合作;文中數字除特別註明外,皆為 2026-11-05 在本機以 Docker 實測所得。)

📬 訂閱 most.tw 電子報

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

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

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

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

加入 LINE 好友