NextPDF Gotenberg 문제 해결
한눈에 보기
섹션 제목: “한눈에 보기”브리지는 실패를 명확하고 빠르게 드러냅니다. 모든 실패는 구조화된 예외로 표현되며, 메시지는 원인을 명시합니다. 이 페이지는 그 목록입니다. 각 실패에 대해 예외 유형, 표시되는 메시지 조각, 코드 경로에서의 정확한 트리거, 해결 방법을 제공합니다.
예외 계열은 다음과 같습니다:
GotenbergConvertException— 변환 계층 실패(구성, 전송 또는 응답)입니다.RuntimeException— 네트워크 트래픽이 발생하기 전에 보안 정책이 발생시키는 검증 계층 실패입니다.ValueError— 인식되지 않는 파일 확장자입니다.InvalidSpkiPinException— 형식이 잘못된 TLS 핀 문자열입니다.
구성 실패
섹션 제목: “구성 실패””Invalid Gotenberg configuration: apiUrl is empty”
섹션 제목: “”Invalid Gotenberg configuration: apiUrl is empty””- 유형:
GotenbergConvertException - 트리거:
convertFile()또는convertString()가GotenbergConfig::isValid()가 false인 상태에서 호출되었습니다. 이는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 스킴이
https가 아닙니다. 이 검사는 대소문자를 구분하지 않으므로HTTPS://은 허용됩니다. - 해결: Gotenberg 앞단에 TLS를 두고 HTTPS 엔드포인트를 구성하십시오. 일반 HTTP는 로컬 개발 환경에서도 거부됩니다.
”Invalid Gotenberg API URL: unable to parse”
섹션 제목: “”Invalid Gotenberg API URL: unable to parse””- 트리거: URL을 스킴과 호스트로 파싱할 수 없습니다.
- 해결: 구문상 유효한 절대 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 범위, 루프백, 링크-로컬 주소를 차단합니다.
- 해결: 브리지의 대상이 라우팅 가능한 공개 주소나 적절히 분리된 서비스 주소가 되도록 하십시오. 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를 조사하십시오. 정상적인 원인으로는 주소를 순환시키는 로드 밸런서가 있습니다. 악의적인 원인으로는 리바인딩 공격이 있습니다. 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 바이트 = 50MiB)보다 큽니다. - 해결: 문서에 실제로 필요하다면
maxFileSize를 올리거나, 업스트림에서 업로드를 거부하십시오. 실제 문서 요구사항이 허용하는 범위에서 상한을 가능한 한 낮게 유지하십시오. 이는 브리지에 내장된 유일한 리소스 한도입니다.
파일 이름 거부
섹션 제목: “파일 이름 거부”파일 이름을 검사합니다. 파일 변환의 경우 파일 이름은 확인된 경로의 베이스네임이며, convertString()의 경우 전달하는 이름입니다. 각 실패는 RuntimeException입니다:
| 메시지 조각 | 트리거 |
|---|---|
must not be empty | 빈 파일 이름 |
path traversal sequences (..) | 이름에 ..이(가) 포함됨 |
forward slashes | 이름에 /이(가) 포함됨 |
backslashes | 이름에 \이(가) 포함됨 |
null bytes | 이름에 NUL 바이트가 포함됨 |
control characters | 이름에 ASCII 제어 문자(0–31)가 포함됨 |
- 해결: 정리된 베이스네임을 전달하십시오.
convertString()의 경우report.docx와 같은 일반 이름을 제공하십시오. 이는 경로가 아니라 형식 감지와 멀티파트 업로드 파일 이름으로 사용됩니다.
”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 핀 고정 전송)가 요청 전송 중에 예외를 던졌습니다. 원인은 연결 거부, 타임아웃, TLS 핸드셰이크 실패 또는 핀 불일치입니다.
- 예외 코드: 하위 클라이언트 예외의 코드입니다.
- 원인: 원래의 PSR-18 클라이언트 예외가 이전 예외로 첨부됩니다.
- 해결: 다음 네 가지를 확인하십시오.
isAvailable()로 서비스 도달 가능성을 확인하고, 네트워크 경로와 TLS 체인을 점검하십시오. 핀 고정이 구성되어 있다면, 서버의 현재 SubjectPublicKeyInfo(SPKI)가 구성된 핀과 일치하는지도 확인하십시오. 인증서 교체 후 핀 불일치가 전형적인 원인입니다. 교체 절차는 /integrations/gotenberg/security-and-operations/ 문서를 참조하십시오.
”cURL transport error (): ”
섹션 제목: “”cURL transport error (): ””- 트리거: cURL 핀 고정 전송의
curl_exec가 0이 아닌 cURL 오류 번호로 실패했거나, 문자열이 아닌 본문을 반환했습니다. - 해결: cURL 오류 번호가 원인(TLS, DNS 확인, 타임아웃, 핀)을 식별합니다. 핀 고정 실패는
CURLOPT_PINNEDPUBLICKEY가 인증서를 거부할 때 여기에 나타납니다. 구성된 핀과 확인된 주소가 최신인지 확인하십시오.
”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가 포함되지 않았습니다. - 해결: 이는 보통 프록시나 게이트웨이가
200과 함께 HTML 오류 또는 리디렉션 페이지를 반환했음을 의미합니다. 브리지는 핀 고정 전송에서 리디렉션 추적을 의도적으로 비활성화하므로,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가 아닌 데이터를 반환했습니다. 응답을 신뢰할 수 없는 것으로 취급하고 서비스를 조사하십시오. 본문을 디스크에 기록하지 마십시오. 브리지는 이를 결과로 반환하지 않습니다.
핀 구성 실패
섹션 제목: “핀 구성 실패””Invalid SPKI pin format: (expected sha256/)”
섹션 제목: “”Invalid SPKI pin format: (expected sha256/)””- 유형:
InvalidSpkiPinException - 트리거: 구성된 핀 문자열이
sha256/또는sha256//로 시작하지 않습니다. - 해결: 각 핀을
sha256/<base64-encoded-spki-hash>형식으로 작성하십시오. 전송은 cURL 네이티브sha256//<base64>형식도 허용합니다. 이 값은 전체 인증서가 아니라 서버 인증서의 SubjectPublicKeyInfo에서 생성하십시오.
”사용 불가라고 표시되지만 서비스는 작동 중입니다”
섹션 제목: “”사용 불가라고 표시되지만 서비스는 작동 중입니다””isAvailable()은 URL이 비어 있거나, HTTPS가 아니거나, private/reserved 주소로 확인되면 네트워크 호출 없이 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 모델과 핀 교체를 설명합니다.
- /integrations/gotenberg/quickstart/ — 전체 catch 순서를 맥락 속에서 보여 줍니다.