10.1 上線前檢查清單:從「能跑」到「敢上線」#
本節目標#
寫完程式碼、功能能跑,只是及格線;真正「上線」需要的不是再多寫一個功能,而是把前面九章散落各處的設定系統性驗證一遍。本節把 DevOps 的關鍵思維——「部署不是事件,而是可重複的程序」——落實為一份可直接照著勾的檢查清單,涵蓋 DNS、HTTPS、安全標頭、CORS、備份、監控、錯誤頁與 API 嚴格模式八個面向。
為何需要檢查清單:人的記憶不可靠#
Atul Gawande 在《The Checklist Manifesto》的核心論點:即使專家也會在例行工作中漏掉已知步驟。飛行有飛行前檢查單、手術有手術安全清單,軟體上線也應有。
上線事故的典型分類:
40% 設定疏漏(忘了開 HTTPS 重定向、忘了 CORS 白名單)
25% 資料風險(無備份、無法還原)
20% 觀測性缺失(出事時不知道,使用者先發現)
15% 權限與模式錯誤(開發模式 API 上到正式環境)
清單的存在意義:把這些「已知會忘的事」外包給一張紙。
檢查清單#
一、DNS 與網域#
- 網域 NS 已指向 Cloudflare(查
dig NS example.com,應回 Cloudflare 的名稱伺服器) - 所有子網域的 DNS 記錄已建立,無遺漏(拼錯一個字母的子域會指向不存在的地方)
- 暫存用途的萬用字元
*.example.com記錄已確認是否真的需要(過寬 = 攻擊面) - TTL 已考前考量:正式環境 300–3600 秒;遷移期間調低以便回滾
- 開啟 Cloudflare Proxy(橘雲)保護源站的記錄,僅必需直連的服務用灰雲
- Email 相關的 SPF、DKIM、DMARC 記錄未被 CDN 遷移破壞
二、HTTPS 強制與 HTTP 重寫#
- Zone 層級已開啟 Always Use HTTPS(HTTP 301 到 HTTPS)
- SSL/TLS 模式為 Full (strict),絕不是 Flexible(Flexible 會讓邊緣到源站走明文,是本書反覆強調的反模式)
- Edge Certificates:Universal SSL 已啟用;若用自有憑證,到期日已設告警
- Tough case:API 子域(如
api.example.com)同樣有憑證覆蓋(SAN 或獨立憑證)
\[ \text{HTTPS 只是把「WWW」那行字換成鎖頭?不是——它是 TLS 握手 + 憑證驗證的家庭作業} \]
三、安全標頭與 Content Security Policy#
安全標頭是「瀏覽器的防火牆」,在 Worker 或 Middleware 統一注入:
- CSP(Content-Security-Policy):限制腳本、樣式、圖片的來源;生產環境不該出現
'unsafe-inline'、'unsafe-eval' - HSTS(Strict-Transport-Security):
max-age=31536000; includeSubDomains,逼瀏覽器永遠用 HTTPS - X-Frame-Options:
DENY或SAMEORIGIN,防點擊劫持 - X-Content-Type-Options:
nosniff,防 MIME 偽裝 - Referrer-Policy:
strict-origin-when-cross-origin - Permissions-Policy:關掉用不到的相機、麥克風、地理定位
用 securityheaders.com 掃一次,等級目標至少 A。
四、CORS#
若前端與 API 不同源(例如 app.example.com 與 api.example.com),CORS 是繞不開的一關:
-
Access-Control-Allow-Origin採明確白名單,絕不是*(尤其 API 帶 cookie 時*直接無效) - Preflight(
OPTIONS)回覆包含Allow-Methods、Allow-Headers,且OPTIONS請求在 Worker 中短路回覆,不打到後端邏輯 -
Allow-Credentials: true時,Origin 必須是具體值 - 用
curl -X OPTIONS -H "Origin: https://app.example.com" -H "Access-Control-Request-Method: POST"實測 preflight
五、資料備份(D1 export)#
Cloudflare D1 是 SQLite 資料庫,做成備份是一個命令的事,但必須是排程化的:
npx wrangler d1 export my-db --remote --output backup-$(date +%F).sql
- 每日自動匯出(GitHub Actions cron 或 Cloudflare Cron Trigger 觸發)
- 備份檔存到 R2 或本地,驗證過至少一次還原練習(沒還原過的備份不算備份)
- 保留多份版本,不只最新一份
- KV 與 R2 的重要資料同樣有匯出策略
檢查清單的狠話:上線前做不到「隨手 一行命令還原到昨天」的資料庫,不上線。
六、監控與告警#
- Cloudflare Analytics / GraphQL Analytics API 已瞭解,能查 5xx 上升、WAF 攔截暴增
- 關鍵端點有可用性檢查(Cloudflare Health Checks 或外部行程如 UptimeRobot)
- 告警能通知到人:Email 之外,至少一條即時通道(Slack / Discord webhook)
- Workers 的 CPU time 使用量沒有觸及方案限制
- Pages 部署失敗的事件有通知(GitHub Actions 失敗郵件或 Slack webhook)
- Workers 的錯誤率已納入觀察(Cloudflare Analytics 或 GraphQL API 拉指標)
告警法則:會吵的告警會被靜音。告警數量、訊息、閾值都要能讓值班的人一秒判斷「要不要爬起來」。
七、自訂錯誤頁#
Cloudflare 出錯時(如 502、522 源站逾時),預設會吐無本地資訊的英文白頁:
- 在 Rules → Custom Error Pages 上傳自訂錯誤頁(500、502、503、522)
- 錯誤頁本身是純靜態、外部資產用絕對 URL(因為出錯時 zone 可能連 stylesheet 都載不到)
- 給使用者下一步指引:狀態頁連結、重試時間
- Pages Functions 中有
onError/catch-all 回覆統一 JSON 錯誤格式
八、API 的開發人員嚴格模式#
最後一項最容易被忘掉:不要把開發者工具放進正式環境。
- API 模式嚴格化:未知 route 回 404 + 統一錯誤體,不是靜默通過
- 錯誤不含堆疊(
stack、SQL error、內部 IP)——那是攻擊者的快捷鍵 - Swagger/OpenAPI 文件若對外公開,正式環境只讀;寫入端點需 Access 鑑證
- Rate limiting 已上(Cloudflare WAF Rate Limiting Rule 或 Worker 層)
-
.env與 secret 掃描:確認沒有 API key 出現在客戶端 bundle(npx gitleaks detect)
執行策略:清單要進 CI#
一張只存在 PDF 裡的清單會死的比程式碼快。把它拆成兩條層次:
| 層次 | 做法 |
|---|---|
| 可自動驗證 | 寫進 CI 腳本:curl -I 查標頭、curl -X OPTIONS 查 CORS、dig 查 DNS |
| 必須人工判斷 | 製作 docs/release-checklist.md,部署 PR 模板夾帶勾選 |
# 前四項清單的 10 秒自動驗證
curl -sI https://example.com | grep -iE 'strict-transport|content-security|location: https'
dig +short NS example.com | grep -c cloudflare
小結與練習#
回顧#
- 上線不是事件而是程序;檢查清單是把「已知會忘的事」外部化。
- 八大檢查面:DNS、HTTPS、安全標頭、CORS、備份、監控、錯誤頁、API 嚴格模式。
- 備份的定義包含「還原演練」;監控的定義包含「告警可行動」。
- 清單的一半應該死在 CI 裡,而不是躺在 PDF 裡。
練習#
- 對你的網站跑 securityheaders.com 與
curl -sI,記下缺的每一項標頭,並在 Worker 中補齊大一個等級。 - 寫一個 GitHub Actions workflow:每日 cron 執行
wrangler d1 export並上傳到 R2 指定前綴。 - 把本節清單中最容易自動化的四項做成一個
scripts/preflight.sh,未通過時 CI 失敗。 - 故意破壞一項(如關掉 Always Use HTTPS),再用你的腳本把它抓出來——驗證清單真的有效。