當產品團隊把瀏覽器儀表板或內部單頁應用接到監聽 TCP 8787 的 OpenClaw 閘道,2026 年最常見的「神秘中斷」往往不是模型 API,而是 CORS。curl 正常,Safari 與 Chrome 卻出現 blocked by CORS policy,多半因為瀏覽器送出的 OPTIONS 預檢在邊緣未被處理,或因回應寫了 Access-Control-Allow-Origin: * 而前端使用 credentials: 'include'。本篇提供顯式來源白名單、簡單與含憑證請求差異、nginx 標頭順序與重複標頭陷阱,以及可貼進工單的 curl 探針,並與 首次執行 PATH、Node 與 LaunchAgent 冒煙、連接埠占用與健康探針對齊、doctor 閘道診斷 對齊,降低跨團隊來回。
讀完可帶走決策表、標頭配方、數值護欄(204 或 200 的 OPTIONS 皆可;含憑證時 禁用萬用字元),以及 MacHTML 公開定價約 每日 16.9 美元 的租用基準,方便在與客戶相同的 Safari 版本上演練。
瀏覽器與 curl 的差異
curl https://gateway.example/health 僅證明可連線,無法證明瀏覽器政策。使用者代理在跨站呼叫會附加 Origin,出現自訂標頭時可能把看似單純的 GET 變成需預檢的 POST,且除非 Access-Control-Allow-Origin 與來源相符(或非憑證情境為 *),否則不會把回應本文交給 JavaScript。把 CORS 視為前端與平台的雙邊合約:前端負責精確字串;平台負責在終結 TLS 的那一躍依序輸出標頭。
請以「協定+主機+連接埠」記錄每個呼叫端——https://app.corp.internal:8443 與 https://app.corp.internal 並非同源。白名單漏寫連接埠會讓只在非預設 TLS 連接埠的預備環境間歇失敗。這也是團隊租用專用 Mac mini、對齊高階主管筆電 Safari 版本的原因。
簡單請求與含憑證請求
不含自訂標頭的簡單 GET 可能略過預檢,但若 JavaScript 要跨來源讀取本文,仍須正確的 Access-Control-Allow-Origin。一旦加入 Authorization、Cookie 或 fetch(..., { credentials: 'include' }),規格要求回傳具體來源——禁止萬用字元,並搭配 Access-Control-Allow-Credentials: true。
若只公開匿名指標,可全程使用 * 並避免 Cookie。混合模式應以 nginx location 切分,避免行銷像素誤繼承管理後台的 CORS 標頭。
OPTIONS 預檢機制
預檢會送 OPTIONS,並帶 Access-Control-Request-Method 與選用的 Access-Control-Request-Headers。閘道須以 Access-Control-Allow-Methods 列出允許動詞(常見 GET,POST,OPTIONS),以 Access-Control-Allow-Headers 大小寫不敏感回傳所請求的名稱,並可用 Access-Control-Max-Age 讓瀏覽器快取握手——開發期常用 300 秒,正式環境視標頭輪替取 600–86400。
回 204 無本文或 200 空本文皆可。失敗樣態含 405(nginx 把 OPTIONS 轉錯上游),或動態回傳來源卻缺少 Vary: Origin 導致 CDN 快取錯誤的 ACAO。
來源白名單與 localhost 陷阱
http://localhost:5173(Vite)與 http://127.0.0.1:5173 是不同來源。本機開發應同時納入,或統一只用一種主機名。SSH 本機轉送把 127.0.0.1:8787 暴露給工程師筆電時,瀏覽器來源仍是線上 SPA 的公開 URL,而非迴環跳——白名單須含 SPA 來源,不能只寫 http://127.0.0.1。
動態反射任意 Origin 省事但危險,除非伺服端以固定集合驗證。建議映射表:來源屬於集合 S 才回傳,否則省略標頭讓瀏覽器失敗閉合。對被拒絕的來源以 INFO 抽樣記錄,資安可稽核濫用而不塞滿磁碟。
nginx 與重複標頭
當 nginx 終結 TLS 並轉送本機 OpenClaw 時,須決定 CORS 由 nginx 或 Node 負責——兩邊都加會產生重複標頭,部分瀏覽器直接拒絕。實務拆分:nginx 處理靜態與行銷頁;若閘道已為 CLI 相容性輸出 API 標頭,可僅由 OpenClaw 處理 API。無論選哪條,都寫進與優雅關機相同的運維手冊,避免事故中各修一遍 CORS。
若在 nginx 終結 HTTP/2,舊設定裡 add_header 可能只在 2xx 附加,除非使用 always。OPTIONS 落入錯誤 server 區塊是 SPA 神秘失敗的第二大原因,僅次於萬用字元與憑證不符。
Vary: Origin、CDN 快取與過期 ACAO
依請求回傳來源時,語意上隨 Origin 變化。若缺少 Vary: Origin,中介快取可能把甲客戶的 Access-Control-Allow-Origin 餵給乙客戶的連線——既是隱蔽資料外洩,也是功能錯誤。部分 CDN 會過度正規化並剝除 Vary;若邊緣規則改寫 CORS,應向供應商開案。
除錯 CORS 時把錯誤本文快取 TTL 壓在 60 秒內,避免壞版在邊緣掛數小時。快取清理須搭配結構化日誌的關聯 ID,即使使用者遮罩截圖也能對齊 HAR 與閘道日誌。
JWT、自訂標頭與工具路由
OpenClaw 工具路由常附加 X-Request-Id 或 X-OpenClaw-Profile 風格標頭。任何不在瀏覽器安全清單內的標頭都會觸發預檢,必須在 Access-Control-Allow-Headers 明列——含憑證流程不能仰賴 *。若 JWT 走 Authorization: Bearer,請確認預檢與實際 POST 的 ACAO 行一致;不一致會出現「沒有 Access-Control-Allow-Origin」的經典錯誤,即使伺服端已處理 POST。
輪替簽章金鑰時,可暫時加倍預檢快取視窗,讓瀏覽器在 15 分鐘 內拾取新標頭名稱,再收緊 max-age,並與 TLS 憑證續約寫在同一維運行事曆。
決策矩陣
| 呼叫端 | 是否含憑證 | ACAO 策略 | 備註 |
|---|---|---|---|
| 公開文件站 | 否 | * 或靜態白名單 | 維持匿名;不使用 Cookie |
| 內部管理 SPA | 是 | 回傳具體來源 | 搭配 Allow-Credentials: true |
| 行動 WebView | 有時 | 自訂 scheme 來源 | 驗證 WebView URL 模式 |
| 第三方 SaaS 嵌入 | 少見 | 靜態合作夥伴來源 | 僅以合約白名單為準 |
可歸檔的 curl 探針
# 預檢(替換主機與路徑)
curl -i -X OPTIONS "https://gw.example/v1/chat" \
-H "Origin: https://app.example" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: authorization,content-type"
# 帶 Origin 的簡單 GET,驗證 GET 上的 ACAO
curl -i "https://gw.example/health" \
-H "Origin: https://app.example"
將完整回應標頭存進工單,每次發布後做 diff。當邊緣 OPTIONS 的 p95 延遲超過 150 ms 而 GET 健康檢查仍在 12 ms 量級,應先查路由到冷上游,而非先怪罪 OpenClaw 本體。
上線檢查清單
- 列舉正式與預備環境 SPA 的每個來源,含協定、主機與連接埠。
- 指定 CORS 標頭的唯一負責方——nginx 或 OpenClaw——並移除重複。
- 在 CI 以與本機一致的
Origin字串執行 OPTIONS 探針。 - 以自動化測試驗證含憑證流程拒絕萬用字元 ACAO。
- 變更連接埠或標頭後重跑
openclaw doctor;確認健康端點仍為 200。 - 在租用的 mini 上擷取 Safari 與 Chrome 的 HAR,保留 90 天。
愈來愈多資安審查要求證明動態來源反射有上界;把白名單檔納入 git 並由平台團隊 CODEOWNERS,可避免「暫時萬用字元」提交變成永久風險敞口。
常見問題
為何 Safari 失敗而 Chrome 正常?
Safari 對重複 CORS 標頭與混合內容更嚴格;請在實體 macOS 硬體上雙引擎驗證。
預檢應永久快取嗎?
不應——使用有界的 Access-Control-Max-Age,讓標頭輪替在分鐘到小時級傳播,而非數週。
WebSocket 是否遵守相同 CORS 規則?
握手為帶 Origin 的 HTTP 升級;請以與 REST 相同的白名單邏輯驗證。
Apple Silicon Mac mini 租用讓你在決策者常用的 Safari/WebKit 堆疊上演練閘道 UI。MacHTML 雲端節點支援以 SSH 跑腳本化 curl 套件,亦可選 VNC 讓設計師即時檢視 Network 面板。閒置功耗常在 12 W 左右,把演練用 mini 開一整週的成本,通常低於把錯誤 CORS 推上線再在董事會演示時回滾。
租用亦可避開重資產採購週期:依公開定價約 每日 16.9 美元,而不必再買一台整合結束後長期閒置的實體機。CORS 工作完成後停機即可;標頭留在 git,硬體不必在帳上跨 36 個月 折舊。
在真實 macOS 閘道上演練 OpenClaw CORS
租用雲端 Mac mini,在合併閘道變更前以 Safari 驗證 OPTIONS、含憑證請求與 nginx 邊緣標頭。