疑難排解
此橋接器會拋出三種例外型別。你攔截到的例外型別會指出失敗原因,以及是否適合重試或改用後援。下方每一段訊息片段皆引自原始碼。
例外階層
標題為「例外階層」的區段| 例外 | 繼承自 | 意義 |
|---|---|---|
CloudflareNotAvailableException | NextPDF\Exception\NextPdfException | 無法連線至邊緣節點,或設定不完整,且沒有可用的後援。 |
CloudflareRenderException | NextPDF\Exception\NextPdfException | Worker 有回應,但算繪失敗(HTTP 錯誤或本文格式異常)。絕不會改用後援。 |
InvalidSpkiPinException | InvalidArgumentException | 已設定的 SPKI pin 字串格式不正確。 |
CloudflareSecurityPolicy 也會在輸入或 URL 違反政策時,直接拋出 RuntimeException。這會發生在送出任何請求之前。
設定與輸入失敗
標題為「設定與輸入失敗」的區段| 訊息片段 | 拋出者 | 原因 | 修正方式 |
|---|---|---|---|
incomplete (missing worker_url or api_token) | 算繪器(透過後援路徑) | workerUrl 或 apiToken 為空 | 兩者都要設定,並以 isValid() 驗證。 |
HTML input exceeds maximum size | CloudflareSecurityPolicy::validate() | HTML 長度超過 maxHtmlSize | 減少輸入內容,或明確調高 maxHtmlSize。 |
Base64 data URI exceeds safety limit | CloudflareSecurityPolicy::validate() | 某個 data:;base64, URI 估算後超過 13631488 個位元組 | 將該資產外部化;不要內嵌大型二進位內容。 |
meta-refresh redirect which could cause SSRF | CloudflareSecurityPolicy::validate() | 某個 <meta http-equiv="refresh"> 標籤 | 移除該標籤;請在算繪 HTML 之外,以伺服器端重新導向處理。 |
Invalid Worker URL | validateWorkerUrl() | URL 無法解析,或缺少 scheme/host | 提供完整的絕對 HTTPS URL。 |
Worker URL must use HTTPS | validateWorkerUrl() | scheme 不是 HTTPS | 使用 https://。 |
private or reserved IP addresses | validateWorkerUrl() | IP 字面值位於 RFC 1918 / 回送位址 / RFC 3927 範圍內 | 指向公開端點。 |
hostname resolves to a private or reserved IP | validateWorkerUrl() | 解析出的 A/AAAA 紀錄為 private/reserved | 修正 DNS;並調查是否可能發生重新繫結。 |
DNS answer changed since validation | assertPinsStillValid() | 主機在檢查與送出之間解析到新的 IP | 重新解析;並視為可能的重新繫結嘗試。 |
Worker 端失敗
標題為「Worker 端失敗」的區段這些屬於 CloudflareRenderException。Worker 有回應,但算繪本身失敗了。這些絕不會觸發本機後援 — 邊緣節點是可連線的。
| 訊息片段 | 原因 |
|---|---|
Cloudflare Worker returned HTTP <code>: <detail> | 非 200 狀態。detail 為 JSON 的 error 欄位,或本文前 200 個位元組。 |
Worker returned empty or invalid PDF data | 二進位回應未以 %PDF 開頭。 |
Worker error: <message> | JSON 回應含有 error 欄位。 |
JSON response missing "pdf" field | JSON 回應不含 pdf 欄位。 |
Invalid base64-encoded PDF in JSON response | pdf 欄位經 base64 解碼後,得到的位元組未以 %PDF 開頭。 |
Invalid JSON response from Worker | Content-Type: application/json,但本文無法解碼為陣列。 |
Unexpected Content-Type from Worker: <type> | 狀態為 200,但 Content-Type 既非 application/pdf 也非 application/json。 |
若你攔截到其中之一,請檢查 Worker 記錄檔。此失敗發生在 Worker 端,而不是此橋接器內。
可連線性與後援失敗
標題為「可連線性與後援失敗」的區段這些屬於 CloudflareNotAvailableException。邊緣節點無法使用,且沒有任何後援產出 PDF。
| 訊息片段 | 原因 | 修正方式 |
|---|---|---|
Cloudflare Worker unavailable: <reason> | 發生傳輸錯誤,且後援已停用 | 啟用 fallbackToLocal 並接上工廠,或修復連線。 |
Artisan is installed but no LocalRendererFactoryInterface was provided | nextpdf/artisan 已存在,但未傳入工廠 | 將 LocalRendererFactoryInterface 傳入建構式。 |
local Chrome fallback (nextpdf/artisan) is not installed | 後援已啟用,但未傳入工廠,且 Artisan 也不存在 | 執行 composer require nextpdf/artisan 並接上工廠。 |
當你提供 PSR-3 記錄器,且後援路徑執行時,此橋接器會先記錄一筆 warning(Cloudflare render failed, attempting fallback),接著記錄一筆 info(Falling back to local renderer)。
傳輸/pinning 失敗
標題為「傳輸/pinning 失敗」的區段| 症狀 | 原因 | 修正方式 |
|---|---|---|
InvalidSpkiPinException: Invalid SPKI pin format | pin 不符合 sha256/<base64>(或 sha256//<base64>)格式 | 修正該 pin 字串。 |
cURL transport error (<n>): <msg> | cURL 層級失敗(TLS、DNS、逾時) | 檢查 cURL 錯誤編號;若已設定 pin,請確認提供的 SPKI 仍受 pin 約束。 |
| 憑證輪替後算繪立即失敗 | 新憑證的 SPKI 不在 pin 集合中 | 在輪替之前,先將新的 SPKI 加入為備用 pin。 |
| 雖然已設定 pin,卻沒有使用受 pin 約束的傳輸 | 未提供 PSR-17 ResponseFactory | 傳入 ResponseFactory;受 pin 約束的傳輸需要它。 |
isAvailable() 的行為
標題為「isAvailable() 的行為」的區段isAvailable() 絕不會拋出例外。當設定無效,或 HEAD 探測失敗、拋出例外時,它會回傳 false。只有當探測回應的狀態低於 500 時,它才會回傳 true。true 的結果只是一項提示:後續的 POST 仍可能因上述任何一種 Worker 端錯誤而失敗。不要把通過的探測當成保證。
速率限制的意外狀況
標題為「速率限制的意外狀況」的區段ApiProtection 限流器會在每個程序各自的記憶體中運作。計數不會在重新啟動後保留,也不會在各 worker 或節點之間共用。因此,若你看到某個客戶端在一個節點被允許、在另一個節點被拒絕,這是預期行為。請在限流器前面接上一個共用儲存區,以達成叢集層級的限制。
另請參閱
標題為「另請參閱」的區段- /integrations/cloudflare/security-and-operations/ — 運維操作手冊,以及這些訊息背後的控制機制。
- /integrations/cloudflare/quickstart/ — 標準的 try/catch 寫法。
- /integrations/cloudflare/production-usage/ — 後援接線的細節。