Skip to main content
CORS 之所以絆住擴充功能作者,是因為規則會隨著執行這次請求的情境而變。同一個 fetch 呼叫,在 content script 裡和在 background 裡表現並不相同。 Extension.js 不會改動這些規則中的任何一條。它確實會為自己的開發伺服器設定標頭,也確實會在開發期修補 CSP。這兩件事都在下面說明。

請求在哪裡執行,就由哪一套規則決定

就網路而言,content script 與頁面共用同一個 origin。host 權限在那裡並不能解除 CORS。這是最常見的一個意外。

把請求移到 background

可靠的做法是讓 background 去 fetch,再把結果送回來:
src/content/scripts.js
src/background.js
這條通道的其餘部分,請閱讀 Messaging

宣告 host 權限

background 只有對 manifest 點名的 origin 才豁免 CORS:
manifest.json
沒有相符的項目時,這次請求就是一次普通的跨來源請求,由伺服器的標頭說了算。 Extension.js 不會拿你程式碼裡的 URL 去驗證 host 權限。缺少的項目要到執行階段才會以一次失敗的請求現形,而不是在建置時。 優先採用更窄的 pattern。<all_urls> 能用,但商店審核者會追問它。

Preflight 請求依然會發生

host 權限拿掉的是對回應的 origin 檢查,它拿不掉 preflight。 帶自訂標頭的請求,或是用了 GETHEADPOST 以外方法的請求,仍然會先送出一個 OPTIONS 請求。伺服器必須回應它。伺服器在你手上時,就放行你送出的方法與標頭。不在你手上時,就把請求保持簡單。

Extension.js 開發伺服器

extension dev 期間,Extension.js 會執行一個本機伺服器,用來提供重新載入的用戶端與熱更新。任何頁面上的 content script 都會撥向它,這本身就是一次跨來源請求,所以伺服器回以:
它也接受任何 Host 標頭。這是一個跑在你自己機器上的開發伺服器。它永遠不會出現在打包後的擴充功能裡,它提供的任何東西也不會進入正式環境。

只在開發期生效的 CSP 修補

除非你自己設定,否則 Manifest v3 不會限制 connect-src。因此一個沒有宣告 content_security_policy 的專案不需要任何修補,開發用的 manifest 帶的就是一般政策:
dist/chromium/manifest.json
一旦你宣告了自己的 connect-src,開發建置就會把本機 origin 附加進去,這樣你的頁面仍然連得上那個 socket:
dist/chromium/manifest.json
這些附加的項目會在 extension build 時移除,你自己的 connect-src 則會保留。所以一份漏掉你 API origin 的嚴格政策,會在開發期通過、在正式環境失敗。當一個 CSP 錯誤一路活到正式環境時,請閱讀 Manifest 拒絕

綁定到另一個主機

在容器裡開發時,把伺服器綁到別處:
瀏覽器撥不通 0.0.0.0,所以 Extension.js 會為用戶端解析出一個連得上的位址,退回值是 127.0.0.1。當瀏覽器在另一台機器上時,請覆寫它:
開發期的 CSP 修補會跟著解析出來的主機走,所以那個 socket URL 始終在放行之列。

遠端腳本與樣式表

擴充功能的 CSP 禁止把遠端 origin 上的腳本載入擴充功能頁面。那不是 CORS,任何標頭都修不好它。請改為把程式碼打包進來。 Extension.js 會在建置期回報這種情況:

症狀與修正

最佳實務

  • 把每一個第三方請求都放進 background,包括今天還能正常運作的那些。
  • host_permissions 裡逐一點名 origin,而不是伸手去拿 <all_urls>
  • 發佈之前先用正式建置測一遍。開發期比較寬鬆是刻意的。

下一步