用 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-corp、mywiki、home-lab。取完記下來,下一步要用。
Step 2|建 Google OAuth Client
這一步在 Google Cloud Console 做,Cloudflare 那邊還不動。
2-1. OAuth consent screen(第一次才要)
- 打開 https://console.cloud.google.com/,選/建一個 project
- 左側選單 → APIs & Services → OAuth consent screen
- User Type:選 External
- 填基本資訊:
- App name:任何你能記得的名字(例
My SSO Gate) - User support email / Developer email:你自己的
- App name:任何你能記得的名字(例
- Scopes 頁面不用動,直接 Save
- Test users 頁面 → + ADD USERS:把你之後要授權登入的 email 加進來
**⚠️ 關鍵踩坑點**:External + Testing 模式下,**只有 Test users 裡的 email 能用這個 OAuth**。之後要放行給任何人登入,都得先在這裡加進去,否則 Google 那關就過不了。
Test users 上限 100 人。個人站 / 小團隊夠用,等需要再 publish 變 Production。
2-2. 建 OAuth Client ID
- 左側 → APIs & Services → Credentials
- 頂部 + CREATE CREDENTIALS → OAuth client ID
- Application type:Web application
- Name:隨便,例如
Cloudflare Access - Authorized redirect URIs → + ADD URI,填:
- CREATE
- 會跳出一個視窗顯示:
- Client ID(長這樣:
123456789-xxxx.apps.googleusercontent.com) - Client Secret(
GOCSPX-xxxxx)
- Client ID(長這樣:
複製這兩個,下一步要用。Client Secret 只會顯示一次,丟了就要重建。
Step 3|在 Cloudflare 把 Google 加成 Identity Provider
從這步開始,我給兩條路:UI 跟 API。API 比較適合要寫自動化腳本的人。
路 A|UI 版
- 一樣在 https://one.dash.cloudflare.com
- 左側 Settings → Authentication
- Login methods → Add new
- 選 Google
- 填 App ID(= Client ID)跟 Client secret
- Save
路 B|API 版
先建一個有這些權限的 API Token(https://dash.cloudflare.com/profile/api-tokens):
Account → Access: Organizations, Identity Providers, and Groups → EditAccount → 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 版
- Zero Trust 主控台 → Access → Applications → Add an application
- 選 Self-hosted
- Application Configuration:
- Name:識別用,例
Admin Dashboard - Session duration:
24 hours(通過後多久不用再認證) - Public hostname → 填你要保護的網域,例
admin.example.com
- Name:識別用,例
- Next → Identity providers 選 Google(只勾 Google)
- 勾選
Instant Auth/Auto redirect to identity provider(重要!勾了就跳過「選登入方式」直接跳 Google) - 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 階段:
- + Add policy
- Policy name:例
Only admins - Action:Allow
- Include:選 Emails → 填 email(多筆用逗號分)
- 或選 Emails ending in:
@yourcompany.com(整個公司 domain 放行)
- 或選 Emails ending in:
- Next → Save
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:白名單內的人能進
用無痕視窗打網址:
- 跳到 Google 登入頁
- 登入 allow-list 裡的 email
- 被放回你的網站,這次顯示正常內容
測 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/)設bypasspolicy,不要 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 登入」。