解決Cloudflare Pages部署紀錄過多導致無法刪除專案的問題
📑 文章目錄
最近要在Cloudflare Pages刪除專案遇到無法刪除的問題,官方文件說這是一個已知問題:
You may not be able to delete your Pages project if it has a high number (over 100) of deployments. The Cloudflare team is tracking this issue.
搜尋網路上有很多自動刪除部署紀錄的方式(包含官方),但我覺得有點麻煩和複雜,所以請Gemini寫一個無腦執行的Python腳本(簡單測個幾次,修正一些錯誤,並加入一些功能)。
注意
本文由 Gemini 3 Pro 語言模型產生。
📌 前言
當 Cloudflare Pages 專案累積超過 100 個部署紀錄時,嘗試刪除專案會出現 Your project has too many deployments to be deleted 錯誤。本教學使用一個安全的 Python 腳本,透過 API 批次清理舊的部署紀錄,讓您能順利刪除專案。
特色:
- ✅ 免安裝依賴:使用 Linux/macOS/Windows 內建 Python 即可執行。
- ✅ 安全機制:保留最近 N 筆紀錄、防無限迴圈、支援 Ctrl+C 強制中斷。
第一步:取得 Account ID(帳戶 ID)
這是您在 Cloudflare 的身分識別碼。
- 登入 Cloudflare Dashboard。
- 點擊左側列表的 Workers & Pages。
- 在頁面的右側欄位(通常要稍微往下滑一點),找到 Account details。
- 複製 Account ID 下方那串英數字串(例如:
b279f1d51...)。
第二步:建立 API Token(API 權限)
我們需要一把專門用來「管理 Cloudflare Pages」的鑰匙。
- 點擊右上角的人頭圖示,選擇 My Profile。
- 點擊左側的 API Tokens。
- 點擊藍色的 Create Token。
- 重要:不要選範本,請點擊最下方的 Create Custom Token 旁邊的 Get started。
- 依照以下設定填寫:
- Token name:隨便取(例如:
CF Pages deployments delete script)。 - Permissions:
- 第一欄選
Account - 第二欄選
Cloudflare Pages - 第三欄選
Edit(重要!必須是編輯權限才能刪除)。
- 第一欄選
- Account Resources:
- 第一欄選
Include - 第二欄選
All accounts(或者指定您的帳戶)。
- 第一欄選
- Token name:隨便取(例如:
- 點擊 Continue to summary → Create Token。
- 複製顯示出來的 API Token(這串密碼只會顯示這一次,請妥善保存)。
第三步:準備 Python 腳本
- 在您的電腦上建立一個新檔案,命名為
delete-cf-pages-deployments.py。 - 將以下程式碼完整複製並貼上存檔:
import urllib.request import urllib.error import json import time import ssl import sys import getpass # ========================================== # Cloudflare Pages 部署紀錄清理工具 v3.0 (翻頁修正版) # 更新日誌:修復當「保留筆數」大於每頁筆數(25)時,程式會誤判提早結束的問題 # ========================================== def get_input(prompt, hidden=False): if hidden: return getpass.getpass(prompt) return input(prompt).strip() def make_request(url, token, method="GET"): try: req = urllib.request.Request( url, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json" }, method=method ) context = ssl.create_default_context() if hasattr(ssl, '_create_unverified_context'): context = ssl._create_unverified_context() with urllib.request.urlopen(req, context=context) as response: if method == "DELETE": return True return json.loads(response.read().decode()) except urllib.error.HTTPError as e: return {"error": e.code, "reason": e.reason} except Exception as e: print(f"❌ 發生未預期的錯誤: {e}") sys.exit(1) def main(): print("\n🧹 Cloudflare Pages 歷史部署清理工具 v3.0") print("========================================") account_id = get_input("請輸入 Account ID: ") project_name = get_input("請輸入 Project Name (專案名稱): ") api_token = get_input("請輸入 API Token (輸入時隱藏): ", hidden=True) keep_str = get_input("請問要保留最近幾筆紀錄?(預設 3,包含線上版): ") try: keep_num = int(keep_str) if keep_str else 3 except ValueError: keep_num = 3 if not all([account_id, project_name, api_token]): print("❌ 錯誤:所有欄位皆為必填。") return base_url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/{project_name}/deployments" print(f"\n🔍 正在連接專案 [{project_name}] ...") # 測試連線 initial_check = make_request(f"{base_url}?per_page=1", api_token) if "error" in initial_check: print(f"❌ 連接失敗!代碼: {initial_check['error']} ({initial_check['reason']})") return print("✅ 連接成功!") print(f"⚠️ 警告:將刪除 [{project_name}] 舊部署,僅保留最近 {keep_num} 筆 (不重複)。") confirm = get_input(f"請輸入 'yes' 確認開始執行: ") if confirm.lower() != 'yes': print("已取消操作。") return total_deleted = 0 kept_ids = set() current_page = 1 # [v3.0 新增] 頁碼控制 while True: # [v3.0 修改] 請求時帶入當前頁碼 request_url = f"{base_url}?per_page=25&page={current_page}" data = make_request(request_url, api_token) deployments = data.get("result", []) # 如果這一頁沒資料了,代表真的找完了 if not deployments: print(f"\n🎉 第 {current_page} 頁無資料,掃描結束!") break print(f"\n📋 讀取第 {current_page} 頁 (本頁 {len(deployments)} 筆)...") batch_deleted = 0 for dep in deployments: d_id = dep['id'] # 1. 檢查是否已經在保留名單中 if d_id in kept_ids: continue # 2. 如果保留名額還沒滿,就加入保留名單 if len(kept_ids) < keep_num: kept_ids.add(d_id) # 這裡不印 log 了,避免大量保留時洗版,只在最後統計顯示 continue # 3. 名額已滿 -> 刪除! result = make_request(f"{base_url}/{d_id}?force=true", api_token, method="DELETE") if result is True: print(f" ✅ 已刪除: {d_id}") batch_deleted += 1 total_deleted += 1 time.sleep(0.2) else: print(f" ⏭️ 跳過: {d_id} (可能是線上版本或鎖定中)") # 雖然刪除失敗(例如線上版),但也算處理過了,不應該卡住 # 但因為線上版通常在第一頁,所以邏輯上不會影響翻頁 # [v3.0 核心邏輯] 翻頁判斷 if batch_deleted > 0: # 情況 A: 這一頁有刪除東西 # 當我們刪除項目後,後面的資料會「遞補」上來填補空缺 # 所以我們應該「停留在同一頁」再檢查一次,看看遞補上來的是不是也要刪 print(f" 🔄 本頁有刪除 {batch_deleted} 筆,將重新掃描本頁面以處理遞補項目...") # current_page 不變 else: # 情況 B: 這一頁全部都保留(或跳過) # 代表這一頁的檢查已經完美結束,我們必須「翻到下一頁」去找更舊的資料 print(f" ✅ 本頁所有項目皆保留,準備翻到下一頁...") current_page += 1 print(f"⏳ 休息 1 秒...") time.sleep(1) print("\n" + "="*40) print(f"🏁 清理完成!") print(f"🛡️ 最終保留: {len(kept_ids)} 筆") print(f"🗑️ 實際刪除: {total_deleted} 筆") print("="*40) if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n\n❌ 使用者強制中斷程式。")
第四步:執行與刪除
開啟終端機。
進入您存放腳本的資料夾(例如
cd Downloads)。執行指令:
python3 delete-cf-pages-deployments.py依照畫面提示輸入:
- Account ID:貼上第一步取得的 ID。
- Project Name:貼上您想刪除的專案名稱(例如
my-blog)。 - API Token:貼上第二步建立的密鑰(輸入時畫面不會顯示,這是正常的,貼上後按 Enter 即可)。
- 保留筆數:建議輸入
3或5(若要完全刪除專案,留少一點比較好刪)。
輸入
yes確認執行。
腳本會開始自動運作,您會看到它一筆一筆地刪除。
第五步:回到 Cloudflare 刪除專案
當腳本跑完並顯示 🏁 清理完成! 後:
- 回到 Cloudflare Dashboard → Workers & Pages。
- 點進該專案 → Settings。
- 滑到最下方點擊 Delete project。
- 這次您應該就能刪除成功了!
💡 小提醒
- 可以中斷嗎?可以。隨時按
Ctrl + C可強制停止腳本,Cloudflare 端也會立刻停止動作,不會繼續刪除。 - 為什麼要設定保留筆數?
- 為了備份(推薦):建議保留 3~5 筆,這樣當新版網站有問題時,您還有舊版本的紀錄可以一鍵還原(Rollback)。
- 為了徹底刪除:如果您是為了刪除整個專案而執行此腳本,可以輸入
0或1,腳本會刪除所有能刪的紀錄,只留下最後一個系統鎖定無法刪除的線上版本(這是正常現象,接著再去網頁版刪除專案即可)。