故障排查
该桥接器会抛出三种异常类型。你捕获到的具体类型,会说明发生了哪类失败,以及是否适合重试或改用回退。下面的每段消息片段都引自源代码。
异常层次结构
标题为“异常层次结构”的章节| 异常 | 继承自 | 含义 |
|---|---|---|
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() | 非 HTTPS 的 scheme | 使用 https://。 |
private or reserved IP addresses | validateWorkerUrl() | 位于 RFC 1918 / 回环地址 / RFC 3927 范围内的 IP 字面量 | 指向公开端点。 |
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> | 带有 error 字段的 JSON 响应。 |
JSON response missing "pdf" field | 不含 pdf 字段的 JSON 响应。 |
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 | 不属于 sha256/<base64>(或 sha256//<base64>)格式的 pin | 修正该 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/ — 回退接线细节。