用 Cloudflare Access 幫你的網站加上 Google 登入閘:任何自架服務都能用的通用教學

你的後台管理頁、部署面板、文件 wiki 想對外但不想讓全世界看到?Cloudflare Access + Google SSO 是最快的解——50 人以內免費、10 分鐘設定、不動你的網站一行程式碼。本文從零帶你走完整流程,附完整 API 腳本跟常見踩坑。

用 Cloudflare Access 幫你的網站加上 Google 登入閘:任何自架服務都能用的通用教學

你有沒有遇過這種情境:

  • 架了一個後台管理頁,想從外面登入用,又怕全世界都看得到
  • 部署面板(Dokploy、Portainer、Coolify 之類)想公開 URL,但不敢放全公網
  • 自己寫的內部文件 / wiki / dashboard 不想做使用者系統,但又不想任人訪問

Cloudflare Access 就是解法。它幫你在網站前面加一道「Google / GitHub / Email PIN」登入閘,不用改你的網站一行程式碼——你的服務照跑,只是外面多了個保鑣。

本文用最常見的 Google SSO 當例子,從零帶你走完整流程。


這方案能做什麼、不能做什麼

  • 把任何 HTTP/HTTPS 網站加上 Google 登入(email allow-list)
  • 不改網站程式,網站本身可以「以為沒這層」照常運作
  • 用 Cloudflare 的全球節點擋流量,連你網站登入頁都看不到
  • 免費給 50 個使用者以內的情境

不能

  • 取代網站自己的使用者系統(CF Access 驗的是「這個人能不能進來」,不是「這個人在你網站裡是誰」)
  • 擋住網站本身的漏洞(Access 擋在外層,進去之後還是要看網站自己的權限)
  • 幫你做精細的 app 內權限(那要 OAuth2 Proxy 或網站自己的 RBAC)

費用現況

  • Zero Trust Free:50 使用者內免費
  • 超過:$7/user/月(差不多)
  • 這裡「使用者」= 每月實際登入過的不重複 email 數

對個人站、小團隊、開源專案維護者,一輩子不會超過


整體流程

參與方有三個,搞清楚誰做什麼很重要:

┌──────────────┐    1. 使用者請求    ┌──────────────┐
│  使用者瀏覽器 │ ──────────────────> │  Cloudflare  │
└──────────────┘                     │   (Access)   │
                                     └──────┬───────┘
                           沒 cookie        │
                                            ▼
                              ┌─────────────────────┐
                              │  Google OAuth 登入  │   ← 2. CF 把你丟給 Google
                              └──────────┬──────────┘
                                         │
                                         ▼
                              ┌─────────────────────┐
                              │  CF 檢查 email      │
                              │  在不在 policy      │   ← 3. 白名單比對
                              │  allow-list 裡      │
                              └──────────┬──────────┘
                                         │ 通過
                                         ▼
                              ┌─────────────────────┐
                              │  發 CF cookie       │   ← 4. 24h 內免再認證
                              │  放行給原網站        │
                              └──────────┬──────────┘
                                         ▼
                                   ┌──────────┐
                                   │ 你的網站  │   ← 5. 照常看到請求
                                   └──────────┘
Code language: PHP (php)

做一次,之後新增網站只做最後兩步(建 Application + Policy)。


前置條件

  • 你的網域已經託管在 Cloudflare(DNS 指向 Cloudflare nameservers)
  • 你的網站能透過 Cloudflare 訪問(任何方式:Cloudflare Tunnel、一般 DNS proxied、Worker)
  • 一個 Google 帳號(隨便哪個,只要有 Google Cloud Console 存取權)

Step 1|決定 Zero Trust 團隊名稱

登入 https://one.dash.cloudflare.com(這是 Zero Trust 專用的主控台,跟一般 dashboard 不同)。

第一次進會要你取一個 team name。一旦取了就是:

https://<你的team>.cloudflareaccess.com
Code language: HTML, XML (xml)

這個 URL 之後會變成登入頁的網址。命名建議:

  • 簡短易記(之後會出現在瀏覽器網址列)
  • 不含個人資訊(不要用真名、email)
  • 全小寫、英數 + 連字號

例子:acme-corpmywikihome-lab取完記下來,下一步要用


Step 2|建 Google OAuth Client

這一步在 Google Cloud Console 做,Cloudflare 那邊還不動。

2-1. OAuth consent screen(第一次才要)

  1. 打開 https://console.cloud.google.com/,選/建一個 project
  2. 左側選單 → APIs & ServicesOAuth consent screen
  3. User Type:選 External
  4. 填基本資訊:
    • App name:任何你能記得的名字(例 My SSO Gate)
    • User support email / Developer email:你自己的
  5. Scopes 頁面不用動,直接 Save
  6. Test users 頁面 → + ADD USERS:把你之後要授權登入的 email 加進來

**⚠️ 關鍵踩坑點**:External + Testing 模式下,**只有 Test users 裡的 email 能用這個 OAuth**。之後要放行給任何人登入,都得先在這裡加進去,否則 Google 那關就過不了。

Test users 上限 100 人。個人站 / 小團隊夠用,等需要再 publish 變 Production。

2-2. 建 OAuth Client ID

  1. 左側 → APIs & ServicesCredentials
  2. 頂部 + CREATE CREDENTIALSOAuth client ID
  3. Application type:Web application
  4. Name:隨便,例如 Cloudflare Access
  5. Authorized redirect URIs+ ADD URI,填:
  6. CREATE
  7. 會跳出一個視窗顯示:
    • Client ID(長這樣:123456789-xxxx.apps.googleusercontent.com)
    • Client Secret(GOCSPX-xxxxx)

複製這兩個,下一步要用。Client Secret 只會顯示一次,丟了就要重建。


Step 3|在 Cloudflare 把 Google 加成 Identity Provider

從這步開始,我給兩條路:UI 跟 API。API 比較適合要寫自動化腳本的人。

路 A|UI 版

  1. 一樣在 https://one.dash.cloudflare.com
  2. 左側 SettingsAuthentication
  3. Login methodsAdd new
  4. Google
  5. App ID(= Client ID)跟 Client secret
  6. Save

路 B|API 版

先建一個有這些權限的 API Token(https://dash.cloudflare.com/profile/api-tokens):

  • Account → Access: Organizations, Identity Providers, and Groups → Edit
  • Account → Access: Apps and Policies → Edit

然後:

ACCOUNT_ID='<你的 CF account ID>'
CF_TOKEN='<剛才建的 API token>'
GOOGLE_CLIENT_ID='<Step 2 的 Client ID>'
GOOGLE_CLIENT_SECRET='<Step 2 的 Client Secret>'

curl -s -X POST \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/identity_providers" \
  -H "Authorization: Bearer $CF_TOKEN" \
  -H "Content-Type: application/json" \
  --data "{
    \"name\": \"Google\",
    \"type\": \"google\",
    \"config\": {
      \"client_id\": \"$GOOGLE_CLIENT_ID\",
      \"client_secret\": \"$GOOGLE_CLIENT_SECRET\"
    }
  }"
Code language: PHP (php)

回應裡會有 IdP 的 id,下一步要用。

**帳號 ID 怎麼拿**:Cloudflare Dashboard 任何 zone 頁面右下角都看得到,或用 token 打 /zones 看回應裡的 account.id


Step 4|建 Access Application

「Application」= 「哪個網域要受保護」

UI 版

  1. Zero Trust 主控台 → AccessApplicationsAdd an application
  2. Self-hosted
  3. Application Configuration:
    • Name:識別用,例 Admin Dashboard
    • Session duration:24 hours(通過後多久不用再認證)
    • Public hostname → 填你要保護的網域,例 admin.example.com
  4. NextIdentity providersGoogle(只勾 Google)
  5. 勾選 Instant Auth / Auto redirect to identity provider(重要!勾了就跳過「選登入方式」直接跳 Google)
  6. Next → 到 Policies 階段(下一步做)

API 版

IDP_ID='<Step 3 回應裡的 id>'

curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps" \
  -H "Authorization: Bearer $CF_TOKEN" \
  -H "Content-Type: application/json" \
  --data "{
    \"name\": \"Admin Dashboard\",
    \"domain\": \"admin.example.com\",
    \"type\": \"self_hosted\",
    \"session_duration\": \"24h\",
    \"allowed_idps\": [\"$IDP_ID\"],
    \"auto_redirect_to_identity\": true
  }"
Code language: PHP (php)

回應裡會有 Application 的 id


Step 5|建 Policy(白名單)

「Policy」= 「誰能通過」

UI 版

在上一步的 Policies 階段:

  1. + Add policy
  2. Policy name:例 Only admins
  3. Action:Allow
  4. Include:選 Emails → 填 email(多筆用逗號分)
    • 或選 Emails ending in:@yourcompany.com(整個公司 domain 放行)
  5. NextSave

API 版

APP_ID='<Step 4 回應裡的 id>'

curl -s -X POST \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps/$APP_ID/policies" \
  -H "Authorization: Bearer $CF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "Only admins",
    "decision": "allow",
    "include": [
      { "email": { "email": "[email protected]" } },
      { "email": { "email": "[email protected]" } }
    ]
  }'
Code language: PHP (php)

Policy 可以用的條件(重要)

include 陣列裡可以放多種規則,符合任一條就通過:

{
  "include": [
    { "email": { "email": "[email protected]" } },          // 個別 email
    { "email_domain": { "domain": "yourcompany.com" } },    // 整個 domain
    { "ip": { "ip": "203.0.113.0/24" } },                   // IP 段
    { "country": { "country_code": "TW" } }                 // 國家
  ]
}
Code language: JSON / JSON with Comments (json)

進階用 require(要同時符合)+ exclude(排除)可組合出複雜規則,例如「台灣 IP 的公司 email,但排除某人」。


驗證流程(5 分鐘走一遍)

測 1:匿名請求被攔截

用 curl 或無痕視窗打你的網域:

curl -sI https://admin.example.com | head -5
Code language: JavaScript (javascript)

預期回:

HTTP/2 302
location: https://<你的team>.cloudflareaccess.com/cdn-cgi/access/login/...
Code language: HTTP (http)

看到 302 + cloudflareaccess.com 就對了。匿名沒辦法進

測 2:白名單內的人能進

用無痕視窗打網址:

  1. 跳到 Google 登入頁
  2. 登入 allow-list 裡的 email
  3. 被放回你的網站,這次顯示正常內容

測 3:白名單外的人被擋

用白名單外的 Google 帳號登入。預期結果:

  • Google 登入成功(因為 Google OAuth 只驗「你是不是這個人」)
  • 回到 CF → CF 看到 email 不在 allow-list → 顯示 Access denied

這代表閘門真的在擋。


常見踩坑

踩坑 1|Google 跳 “Access blocked: This app has not completed the Google verification process”

原因:OAuth consent screen 是 External + Testing 模式,登入的 email 沒加進 Test users 清單。

解法:Google Cloud Console → OAuth consent screen → Test users → 加進去。立刻生效。

踩坑 2|登入 Google 後卡在白畫面或重定向迴圈

原因:Authorized redirect URIs 填錯。CF 會用 https://<team>.cloudflareaccess.com/cdn-cgi/access/callback,你填的要完全一致(大小寫、結尾斜線、協議)。

解法:回 Google Credentials 頁編輯 OAuth client,確認 URI 正確。

踩坑 3|網站某些 API call 被 Access 擋掉(CORS / API client 無法登入)

原因:CF Access 預設擋所有請求。有些 API 客戶端(行動 App、CLI 工具、webhook callback)沒辦法走瀏覽器 Google 登入流程。

解法(任選):

  • Service Token:CF Access 可以發一組長期 token 給機器人用,header 帶 CF-Access-Client-Id + CF-Access-Client-Secret 就能通過
  • Bypass 規則:對特定 path(例如 /api/webhook/)設 bypass policy,不要 Access
  • 分 subdomain:面板走 admin.example.com(有 Access),API 走 api.example.com(自己做 auth)

踩坑 4|設定完過幾分鐘,Google 登入成功後還是 deny

原因:瀏覽器還記得舊的 CF_Authorization cookie。

解法:清 <team>.cloudflareaccess.com 跟你網站 domain 的 cookies,或開無痕重試。


進階:之後擴充會遇到的事

多加一個人

Policy 隨時能編輯,加一行 email 存檔即可。UI 或 API 都一分鐘搞定。

多加一個網站

Step 4 + Step 5 重做一次(換網域、可重用同一個 Google IdP)。你會累積一堆 Access Application,每個都共用同一個 IdP,不用每個網站都建一次 Google OAuth。

把 Google OAuth 從 Testing 改成 Production

Testing 模式的限制:

  • 只有 Test users 裡的 email 能用
  • 同意畫面會警告「未驗證的 app」
  • Test users 上限 100

要解除這些,要去 OAuth consent screen PUBLISH APP,送 Google 審核。個人 / 公司內部用不強求——Testing 就能用。

真的要 publish,要填隱私權政策網址、用途說明、domain 驗證等等,準備 1-2 週審核期。

50 人免費額度用完

到了要升級 Zero Trust Standard,約 $7/user/月(實際看官網)。或換個方向:用公司的 Google Workspace、Okta、Azure AD 做 SAML 整合,這時候 CF Access 不計算個別使用者。


什麼場合適合這樣做、什麼不適合

適合

  • 內部管理面板:Dokploy、Portainer、Grafana、Kibana 等
  • 團隊共用的 dev tools:Jenkins、Argo CD、Jupyter、VS Code server
  • 私人文件:Outline、BookStack、HedgeDoc
  • 開發環境:preview 站、staging、internal API docs
  • 你一個人 side project 的後台:個人部落格 admin、知識庫

不適合

  • 給終端用戶的產品:公開 SaaS、電商、社群(要的是「註冊 / 登入 / 訂閱」,不是白名單閘)
  • 有複雜角色管理的 App:需要 admin / editor / viewer 分權限——CF Access 不管網站內的權限,只管「放不放進來」
  • API / SDK 客戶端為主的服務:行動 App、第三方整合,走瀏覽器 OAuth 不現實

結語

  • 一次設定 15 分鐘,之後新網站 1 分鐘
  • 免費 50 人,家用 / side project 一輩子不會超過
  • 不改網站程式碼,純外掛式保護
  • 白名單 policy 隨時能改,不用重建流程

對自架服務群體 CP 值最高的一個防護層——不上這個,等於把你 Dokploy / Grafana / Portainer 登入頁掛到全網讓人慢慢刷。

照著上面走一次,以後你的所有內部工具都能「用 Google 登入」。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *