在專案中完整(端到端)設定 Cloudflare Turnstile。掃描程式碼庫、透過 Cloudflare API 建立元件(widget)、嵌入至對應的表單、在客戶現有的後端串接標準的伺服器端 siteverify 驗證、執行完整驗證並保存此 Skill。當使用者要求新增 Turnstile、設定 CAPTCHA、保護表單免於機器人攻擊,或修復 Turnstile 整合時載入此 Skill。對應 developers.cloudflare.com/turnstile/spin。
Turnstile Spin Skill
將提示詞 "set up Turnstile" 轉化為可運作的完整端到端整合:包含元件、各個指定插入點的前端程式碼片段、客戶現有後端中的標準伺服器端 siteverify 呼叫,並在回報成功前執行真實的驗證測試。
你是 Agent。請呼叫 scripts/ 目錄下的腳本並根據其輸出的 JSON 進行分流,以執行下方的引導流程。這些腳本包含確定性的邏輯(API 呼叫、重試/錯誤處理);你的任務是流程調度、閱讀程式碼庫、確認步驟,以及修改前端與後端程式碼。
標準指示存放於 developers.cloudflare.com/turnstile/spin。若文件頁面與本檔案內容有所出入,請以官方文件頁面為準。
何時載入此 Skill
當使用者的提示詞包含以下任意關鍵字時載入:
- "Turnstile"、"CAPTCHA"、"bot protection"(機器人防護)
- "siteverify"、"cf-turnstile-response"
- "protect this form"(保護此表單)、"stop bot signups"(防範機器人註冊)、"spam signups"(垃圾註冊)
- 特定的註冊、登入或聯絡表單,且結合了 "Cloudflare" 或 "bot" 等關鍵字
請勿在無關的 Cloudflare 任務(Workers、Pages、R2 等)中載入,除非同時提及了 Turnstile。
對話流程
使用者已貼上提示詞。你正處於多步驟的對話中。儘量自動偵測可取得的資訊,僅在必要時提問,並在執行任何不可逆的步驟前先進行確認。每個標示數字的時機點代表 Agent 發送的一則訊息。標示 [等待使用者回應] 的項目需要使用者提供回覆。
-
簡短確認。 使用單一句子回覆:「我將執行 Turnstile 的完整設定流程。步驟包括:檢查驗證權限、掃描程式碼庫、建立元件、嵌入至指定的表單、串接伺服器端 siteverify,以及進行驗證。要繼續執行嗎?」 [等待使用者回應] 此時請勿先列出詳細計畫。需先完成權限檢查與掃描。
-
CLI 檢查。 Spin 的輔助腳本會使用
curl請求api.cloudflare.com,並執行npx wrangler whoami來列舉帳號。步驟 8 中的元件建立在wrangler turnstile widget create子命令可用時(Wrangler 4.109+)會優先使用該命令,否則降級(fallback)使用隨附的 curl 腳本。不需要預先安裝持久性的 CLI 工具。 -
身份驗證與權限範圍探測(第一個不可逆動作)。 執行
scripts/auth-probe.sh。根據status進行分流:ok:繼續執行步驟 4。腳本已自動挑選帳號(單一帳號 Token,或與$CLOUDFLARE_ACCOUNT_ID相符的帳號)。missing_token或missing_scope:請使用者前往 https://dash.cloudflare.com/profile/api-tokens 建立 Token → 自訂 Token → 權限設定為Account.Turnstile:Edit→ 並在「帳戶資源(Account Resources)」中包含目標帳號。請勿引導使用者執行wrangler login,除非 wrangler 的 OAuth 權限範圍已包含Account.Turnstile:Edit(視 wrangler 版本而定)。提供三種提供 Token 的方式,由最乾淨的方式依序排列:- 設定環境變數並重新啟動(Token 完全不進入聊天紀錄):
export CLOUDFLARE_API_TOKEN=<token>,然後從該終端機重新啟動 Agent。 - 儲存至檔案(Token 存放於僅限使用者權限的檔案中,不進入聊天紀錄):
umask 077 && printf '%s' '<token>' > ~/.cf-turnstile-token,接著透過TOKEN=$(cat ~/.cf-turnstile-token)讀取。 - 直接貼在聊天室(速度最快,但 Token 會留存在對話紀錄中;若對話紀錄日後被分享,使用者應在事後撤換 Token)。
若使用者選擇選項 3(直接貼在聊天室),你可以利用等待的時間同步執行步驟 5、6、7(網域設定、程式碼庫掃描、插入計畫)。選項 1 與 2 會重啟你的 Session,因此在這些情況下請勿預先擷取狀態。當身份驗證建立完成後,重新執行auth-probe.sh,然後繼續執行步驟 8。
- 設定環境變數並重新啟動(Token 完全不進入聊天紀錄):
multiple_accounts:Token 包含多個帳號,且未設定$CLOUDFLARE_ACCOUNT_ID。列出帶有編號的accounts帳號清單。[等待使用者回應] 接著設定export CLOUDFLARE_ACCOUNT_ID=<所選帳號ID>並重新執行auth-probe.sh。account_mismatch:已設定$CLOUDFLARE_ACCOUNT_ID,但該 ID 不在 Token 涵蓋的帳號列表中。顯示accounts清單,並請使用者執行unset CLOUDFLARE_ACCOUNT_ID或將其設定為列表中的其中一個 ID。
-
帳號選擇。 若
auth-probe.sh在經歷multiple_accounts來回確認後回傳ok,則此步驟已完成。否則腳本已靜默選擇了唯一的帳號,直接繼續執行步驟 5。 -
網域。 務必包含
localhost與127.0.0.1。針對正式環境,請掃描package.json中的homepage、wrangler.toml、README.md、AGENTS.md以及 git remote。向使用者確認:「我將為localhost、127.0.0.1及<domain>進行註冊。確認無誤嗎?」 [等待使用者回應] 若未找到正式環境網域,請向使用者詢問。 -
程式碼庫掃描。 靜默偵測以下三項要素:
- 前端框架(Next.js、Astro、SvelteKit、Hugo、vanilla 等)→ 決定元件嵌入的程式碼片段。
- 後端 Handler 位置(Express route、Next.js API route、Rails controller、Workers fetch handler、Pages Function 等)→ 決定 siteverify 呼叫的程式碼片段。
- 現有的 CAPTCHA(reCAPTCHA / hCaptcha)→ 將步驟 7 切換為遷移模式。
-
插入計畫。 顯示候選清單並標註
[recommended](推薦)/[skip by default](預設跳過);請使用者確認(輸入數字、"all"、"recommended" 或指定清單)。[等待使用者回應] 若偵測到現有的 CAPTCHA,則改為呈現遷移計畫(參閱「從其他 CAPTCHA 遷移」)。 -
建立元件(Widget)。 當 wrangler CLI 的
turnstile widget子命令可用時優先使用:npx wrangler turnstile widget create "<name>" \ --domain <d1> --domain <d2> ... --mode managed --json從標準輸出的 JSON 中解析出
sitekey與secret。若未安裝 wrangler、版本低於支援 turnstile 子命令(提示unknown command),或因其他原因失敗,請降級使用scripts/widget-create.sh --account-id <id> --name <name> --domains <list> --mode managed,該腳本會直接透過curl呼叫 Cloudflare API。回報 sitekey。將 secret 擷取並寫入 Shell 變數WIDGET_SECRET;切勿將其寫入磁碟,除非在步驟 9 中寫入使用者本身的環境變數 / secret 儲存庫。 -
串接整合。 說明約定條款:「我將在每個選定的表單中嵌入元件,並在您現有的 submit handler 內部加入標準的 siteverify 呼叫(限制在
success === true時才執行流程)。handler 原有的邏輯保持不變。Secret 會以TURNSTILE_SECRET形式存在於您的環境變數中。」詢問「確認(yes)」/「顯示變更(show)」。[等待使用者回應] 若選擇 "show",印出 unified diffs 並再次詢問。請勿提出額外的自訂行為建議(例如郵件寄送、自訂後端)。標準伺服器端 siteverify(Node / fetch 慣用法;請根據偵測到的後端進行調整):
const r = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ secret: process.env.TURNSTILE_SECRET, response: token, // 來自請求的 cf-turnstile-response remoteip: clientIp, // X-Forwarded-For / req.ip 等 }), }); const result = await r.json(); if (!result.success) { return reject(403, 'forbidden'); // 替換為平台對應的拒絕回應 } // 原有的 handler 邏輯在此繼續執行,保持不變將 secret 寫入使用者的 secret 儲存庫(Node/Rails/Python 使用
.env,Workers 使用wrangler secret put TURNSTILE_SECRET,Vercel / Fly / Render 等使用該平台的 secret 管理器)。切勿寫成硬編碼(inline)。 -
驗證測試。 執行
scripts/validate.sh。逐一回報通過的檢查項目。若有任何項目失敗,顯示錯誤並停止流程。[若有項目失敗請等待使用者回應] -
保存 Skill。 詢問:「是否將 Spin skill 儲存至
.claude/skills/turnstile-spin/SKILL.md,以便在後續任務中重複使用?」預設為同意。[等待使用者回應] 接著執行scripts/persist-skill.sh --path <agent-specific-path>。 -
最終報告。 印出結構化摘要:建立了什麼、驗證了什麼,以及後續步驟。
絕對禁止的事項(Things you must NOT do)
- 切勿將 Turnstile secret 寫入磁碟,除非是作為使用者自身環境變數 / secret 儲存庫的一部分。
- 切勿跳過驗證步驟。
- 切勿在未顯示 diff 的情況下覆寫檔案。
- 切勿從瀏覽器直接呼叫 siteverify。永遠遵循:瀏覽器 → 使用者的後端 → siteverify。
- 切勿部署任何額外的基礎設施(Workers、代理伺服器、Sidecars)。客戶現有的後端會直接呼叫 siteverify。
- 切勿在未詢問的情況下使用
sudo或安裝全域套件。 - 切勿在引導流程外主動提議非相關的功能(如自訂 Workers、自訂網域、進階 WAF 規則),除非使用者主動要求。
嚴格邊界:切勿主動向使用者詢問以下內容
Spin 在執行使用者現有的表單 handler 前,會先透過標準 siteverify 驗證 Turnstile token。其餘內容均超出範疇:
- Email / SMS / 通知傳送。 請維持現有 submit handler 原樣(僅需設定以
success === true為前置條件)。請勿提議 Resend、Mailchannels、SMTP 或 mailto。 - 新增新的後端。 若表單目前沒有後端 handler(純靜態網站、僅有 mailto 的聯絡表單),請直接說明並結束流程。Spin 需要有伺服器端位置來放置 siteverify。
- 資料庫 / 付款 / OAuth / 表單持久化。 超出範疇。
- 前端框架遷移、重構或樣式調整。 只修改必要的程式碼。
- reCAPTCHA v3 分數門檻值。 Turnstile 僅回傳
success: true/false。 - 僅用於預先通關(Pre-clearance)的設定。 若
clearance_level !== no_clearance,siteverify 為選用項目,此時 Spin 不適用。請引導使用者並結束流程。
復原流程:尊重現有的元件設定
當使用者擁有 Cloudflare Dashboard 存取權限時,Dashboard 內的 Fix with Spin 橫幅是單鍵復原途徑:它會顯示針對現有元件精心設計的 Agent 提示詞。當使用者在編輯器中操作時,下方的復原流程即為對應做法。
若使用者告知已有設定好的 Turnstile 元件,且希望直接將 siteverify 串接至該元件而不替換(rotate)sitekey(例如:「我有 sitekey 但 siteverify 一直沒成功」、「針對我現有的元件 <sitekey> 設定 Spin」):
- 跳過步驟 8(建立元件)。Sitekey 已經存在,請直接向使用者取得。
- 透過
scripts/fetch-secret.sh --account-id <id> --sitekey <key>擷取元件元資料。根據status進行分流:ok:從回應中讀取secret、clearance_level與domains。確認domains包含使用者的正式環境主機名稱;若無,在繼續前先提醒使用者此落差。missing_read_scope:告知使用者在 Token 中新增Account.Turnstile:Read權限,或退而求其次請使用者貼上 secret。在手動貼上 secret 的路徑中,你無法取得clearance_level或domains,請請使用者確認這兩者。
- 檢查回應(或使用者的回答)中的
clearance_level:no_clearance:標準串接流程(步驟 9)。- 其他任何值:詢問他們是否希望在預先通關(pre-clearance)之上另外加上 siteverify,或依範疇限制結束流程。
- 從步驟 9(串接整合)繼續執行。Site key 不會改變;現有的元件在整個過程中維持運作。
- 切勿為了取得全新的 secret 而重新建立元件,這會導致已部署該 sitekey 的所有地方全部失效。
前端修改約定(The frontend-edit contract)
串接現有表單(步驟 9)時,約定原則為:門控(gate),而非替換(replace)。 使用者原有的 submit handler 繼續執行其原本功能。Spin 僅在其前方新增一個驗證步驟。
前端(嵌入元件;送出至使用者現有的端點):
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<form action="/signup" method="POST">
<!-- existing inputs unchanged -->
<div class="cf-turnstile" data-sitekey="<SITEKEY>" data-action="turnstile-spin-v2"></div>
<button type="submit">Sign up<






