NextPDF Gotenberg 故障排查
桥接器会尽早明确地失败。每次失败都会表现为带有明确类型的异常,异常消息会指出原因。本页就是这份故障目录。针对每一种失败,本页会列出异常类型、你会看到的消息片段、代码路径中的确切触发条件以及修复方式。
异常类别如下:
GotenbergConvertException— 转换层失败(配置、传输或响应)。RuntimeException— 验证层失败,由安全策略在产生任何网络流量之前抛出。ValueError— 无法识别的扩展名。InvalidSpkiPinException— 格式不正确的 TLS pin 字符串。
配置失败
标题为“配置失败”的章节”Invalid Gotenberg configuration: apiUrl is empty”
标题为“”Invalid Gotenberg configuration: apiUrl is empty””的章节- 类型:
GotenbergConvertException - 触发条件:在
GotenbergConfig::isValid()返回 false 时调用了convertFile()或convertString()。当apiUrl为空字符串时就会发生。 - 修正:提供一个非空的 HTTPS URL。如果你用
fromArray()构建配置,请注意:当api_url缺失或不是字符串时,它会静默地替换为''。请在启动路径中验证配置来源。
URL 与地址(SSRF)失败
标题为“URL 与地址(SSRF)失败”的章节这些异常由安全策略产生,用于防范服务器端请求伪造(SSRF)。它们会在发送任何请求之前抛出,并且都是纯粹的 RuntimeException。
“Gotenberg API URL must use HTTPS (got: http)”
标题为““Gotenberg API URL must use HTTPS (got: http)””的章节- 触发条件:配置的 URL scheme 不是
https。此检查不区分大小写,因此HTTPS://会被接受。 - 修正:在 Gotenberg 前端接入 TLS,并配置 HTTPS 端点。即使在本地开发环境中,纯 HTTP 也会被拒绝。
“Invalid Gotenberg API URL: unable to parse”
标题为““Invalid Gotenberg API URL: unable to parse””的章节- 触发条件:该 URL 无法解析出 scheme 和主机。
- 修正:提供语法有效的绝对 URL,例如
https://gotenberg.example.com:3000。
“Gotenberg API URL must not resolve to a private or reserved IP address”
标题为““Gotenberg API URL must not resolve to a private or reserved IP address””的章节- 触发条件:主机是私有或保留的 IP 字面地址,或主机名(通过所有 A/AAAA 记录)解析到了任何私有或保留地址。这会拦截 RFC 1918 范围、loopback 以及 link-local 地址。
- 修正:将桥接器指向可路由的公开地址,或经过适当网络分段的服务地址。如果你的 Gotenberg 有意部署在私有网络中,桥接器的 SSRF 防护会按设计拒绝它。请通过防护可接受的地址暴露该服务,然后用网络访问控制与认证保护这条路径。请参阅 /integrations/gotenberg/security-and-operations/.
”Gotenberg API URL DNS answer changed since validation — possible DNS rebinding attack”
标题为“”Gotenberg API URL DNS answer changed since validation — possible DNS rebinding attack””的章节- 触发条件:在初次验证与发送请求之间,一次新的 DNS 解析返回了不在原先已验证集合中的地址。
- 修正:这表示 time-of-check/time-of-use 防护被触发。请检查该主机的 DNS。一个合理原因是负载均衡器在轮换地址;一个恶意原因则是重新绑定(rebinding)攻击。请为 Gotenberg 端点使用一个稳定地址,或一个具有稳定记录集的名称。
输入验证失败
标题为“输入验证失败”的章节这些异常由安全策略在请求发出之前抛出。除非另有说明,每个都是纯粹的 RuntimeException。
“File not found or not readable: ”
标题为““File not found or not readable: ””的章节- 类型:
GotenbergConvertException - 触发条件:
convertFile()无法规范化路径,或解析后的路径不是可读的普通文件。目录也会触发这种情况。 - 修正:传入指向已存在且可读文件的路径。该路径会先通过
realpath()规范化,这也能阻止路径穿越。
“File size ( bytes) exceeds maximum allowed size ( bytes)”
标题为““File size ( bytes) exceeds maximum allowed size ( bytes)””的章节- 触发条件:输入超过
maxFileSize(默认 52,428,800 字节 = 50 MiB)。 - 修正:如果确实需要更大的文件,请提高
maxFileSize,或在上游就拒绝该上传。请将上限保持在实际文件所允许的最低值。这是桥接器唯一内置的资源限制。
文件名拒绝
标题为“文件名拒绝”的章节文件名会被检查。对于文件转换,文件名是解析后路径的基本名;对于 convertString(),则是你传入的名称。以下每一项都是 RuntimeException:
| 消息片段 | 触发条件 |
|---|---|
must not be empty | 空白文件名 |
path traversal sequences (..) | 名称包含 .. |
forward slashes | 名称包含 / |
backslashes | 名称包含 \ |
null bytes | 名称包含 NUL 字节 |
control characters | 名称包含 ASCII 控制字符(0–31) |
- 修正:传入干净的基本名。对于
convertString(),请提供简单的名称,例如report.docx。它用于格式检测,并作为 multipart 上传的文件名,而不是路径。
“Unknown office format extension: ”
标题为““Unknown office format extension: ””的章节- 类型:
ValueError - 触发条件:扩展名不是
docx、xlsx、pptx、odt、ods、odp其中之一(不区分大小写,允许开头的点)。 - 修正:只转换这六种可识别的格式。桥接器不会识别旧版二进制格式(
.doc、.xls、.ppt)、.rtf、.csv、纯文本或图像。在你调用桥接器之前,请先将这些输入转换为可识别的格式,或通过另一条路径处理它们。
传输与响应失败
标题为“传输与响应失败”的章节这些全部是 GotenbergConvertException。
“Gotenberg HTTP request failed: ”
标题为““Gotenberg HTTP request failed: ””的章节- 触发条件:PSR-18 客户端(或使用 cURL pin 的传输)在发送请求时抛出。原因可能是连接被拒、超时、TLS 握手失败,或 pin 不匹配。
- 异常代码:底层客户端异常的代码。
- 原因:原始的 PSR-18 客户端异常会作为前一个异常(previous exception)附加。
- 修正:依次检查四件事。用
isAvailable()检查服务是否可达。检查网络路径。检查 TLS 链。此外,如果已配置 pinning,请检查服务器当前的 SubjectPublicKeyInfo(SPKI)是否与某个已配置的 pin 匹配。证书轮换后 pin 不匹配是最典型的原因。轮换流程请参阅 /integrations/gotenberg/security-and-operations/.
“cURL transport error (): ”
标题为““cURL transport error (): ””的章节- 触发条件:使用 cURL pin 的传输中,
curl_exec因非零 cURL 错误编号而失败,或返回了非字符串主体。 - 修正:cURL 错误编号可以指明原因(TLS、解析、超时、pin)。当
CURLOPT_PINNEDPUBLICKEY拒绝证书时,pinning 失败会在这里体现。请确认已配置的 pin 与解析后的地址都是最新的。
“Gotenberg conversion failed with HTTP : ”
标题为““Gotenberg conversion failed with HTTP : ””的章节- 触发条件:响应状态不是
200。响应主体会被纳入,并截断到前 500 个字符;超出时会附上省略号。 - 修正:读取纳入的响应主体。Gotenberg 的错误消息会说明转换被拒的原因:不支持的文件内容、内部 LibreOffice 失败,或
401、403上的认证被拒。401/403表示apiKey缺失或错误。5xx是服务端失败,适合纳入有界重试。
“Unexpected Content-Type from Gotenberg: (expected application/pdf)”
标题为““Unexpected Content-Type from Gotenberg: (expected application/pdf)””的章节- 触发条件:状态为
200,但响应的Content-Type不包含application/pdf。 - 修正:这通常表示某个 proxy 或网关以
200返回了 HTML 错误页或重定向页。桥接器会在使用 pin 的传输中刻意停用重定向跟随,因此3xx不会被静默地跟随到未经检查的主机。主体以错误类型送达,说明你与 Gotenberg 之间存在干扰。请检查网络路径。
“Response body does not start with %PDF header — invalid PDF data”
标题为““Response body does not start with %PDF header — invalid PDF data””的章节- 触发条件:状态为
200、Content-Type可接受,但主体并未以%PDF签名开头。 - 修正:上游返回了一个标头看似正确、实际却不是 PDF 的内容。请将该响应视为不可信,并调查该服务。请勿将该主体写入磁盘。桥接器会拒绝将它作为结果返回。
Pin 配置失败
标题为“Pin 配置失败”的章节”Invalid SPKI pin format: (expected sha256/)”
标题为“”Invalid SPKI pin format: (expected sha256/)””的章节- 类型:
InvalidSpkiPinException - 触发条件:配置的 pin 字符串不是以
sha256/或sha256//开头。 - 修正:将每个 pin 格式化为
sha256/<base64-encoded-spki-hash>。该传输也接受 cURL 原生的sha256//<base64>形式。请从服务器证书的 SubjectPublicKeyInfo 生成该值,而不是从整张证书生成。
“It says unavailable but the service is up”
标题为““It says unavailable but the service is up””的章节当 URL 为空、不是 HTTPS,或解析到 private/reserved 地址时,isAvailable() 会在不发出任何网络调用的情况下返回 false。发生任何网络错误,或 /health 返回 500 或以上时,它也会返回 false;在这些情况下,它会捕获错误而不是抛出。请按顺序检查:
- 配置的 URL 非空且为 HTTPS。
- 主机不会解析到 private/reserved 地址(即使只是探测,SSRF 防护也会拒绝它)。
- 应用程序主机可以访问
<apiUrl>/health,且返回低于500的状态。
另请参阅
标题为“另请参阅”的章节- /integrations/gotenberg/configuration/ — 每个选项与传输选择规则。
- /integrations/gotenberg/production-usage/ — 重试原则与失败处理契约。
- /integrations/gotenberg/security-and-operations/ — SSRF 模型与 pin 轮换。
- /integrations/gotenberg/quickstart/ — 结合上下文展示完整的 catch 顺序。