Skip to content

Configure NextPDF Gotenberg

Configuration has two parts: the immutable GotenbergConfig value object, which describes the service and its limits, and the GotenbergBridge constructor, which receives the PHP Standard Recommendation (PSR) collaborators for Hypertext Transfer Protocol (HTTP). You provide both through explicit constructor injection. The package has no global state, reads no environment variables, and defines no hidden default endpoint.

GotenbergConfig is a final readonly value object. Create it directly with named arguments, or build it from an associative array with GotenbergConfig::fromArray().

FieldTypeDefaultEffect
apiUrlstring''Base Uniform Resource Locator (URL) for the Gotenberg service. Required: an empty value makes the config invalid and every conversion fails fast. Must use Hypertext Transfer Protocol Secure (HTTPS).
timeoutint30Hard transfer timeout in seconds. The cURL-pinned transport applies it when selected.
maxFileSizeint52_428_800Maximum input size in bytes (50 MiB). Inputs larger than this are rejected before any request.
apiKeystring''Bearer token. When non-empty it is sent as an Authorization: Bearer <token> header. Marked #[\SensitiveParameter] so it is redacted in stack traces.
pinnedPublicKeyslist<string>[]Primary Transport Layer Security (TLS) SubjectPublicKeyInfo (SPKI) pins in sha256/<base64> form. Empty disables pinning.
backupPublicKeyslist<string>[]Backup TLS SPKI pins, kept separate so rotation can be validated independently.
<?php
declare(strict_types=1);
use NextPDF\Gotenberg\GotenbergConfig;
$config = new GotenbergConfig(
apiUrl: 'https://gotenberg.example.com',
timeout: 60,
maxFileSize: 20 * 1024 * 1024,
apiKey: $secretFromYourSecretStore,
);

fromArray() accepts snake_case keys and ignores malformed values instead of throwing. Non-string api_url becomes ''. Non-int timeout falls back to 30. Non-int max_file_size falls back to the 50 MiB default. Non-array pin lists become []. Non-string entries inside the pin arrays are dropped.

<?php
declare(strict_types=1);
use NextPDF\Gotenberg\GotenbergConfig;
$config = GotenbergConfig::fromArray([
'api_url' => 'https://gotenberg.example.com',
'timeout' => 45,
'max_file_size' => 20_000_000,
'api_key' => $secretFromYourSecretStore,
'pinned_public_keys' => ['sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg='],
'backup_public_keys' => ['sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys='],
]);

This forgiving parsing is deliberate. You can pass a framework configuration array directly, without a pre-validation layer, and still get a well-typed object. It does not verify that the URL is reachable or that the pins are correct. The bridge performs those checks at conversion time.

isValid() returns true only when apiUrl is a non-empty string. It performs no network or scheme checks. The security policy handles HTTPS and private-address screening at conversion time. If the config is invalid, convertFile() and convertString() throw a GotenbergConvertException with the message Invalid Gotenberg configuration: apiUrl is empty. An invalid config also makes isAvailable() return false without any network call.

GotenbergBridge receives the configuration plus the PSR collaborators:

ArgumentTypeRequiredEffect
configGotenbergConfigyesThe service descriptor and limits described above.
httpClientPsr\Http\Client\ClientInterfaceyesThe PSR-18 client used for the health probe and fallback transport.
requestFactoryPsr\Http\Message\RequestFactoryInterfaceyesBuilds the PSR-7 request.
streamFactoryPsr\Http\Message\StreamFactoryInterfaceyesBuilds the request body stream.
loggerPsr\Log\LoggerInterface|nullno (default null)When supplied, logs one debug-level entry per conversion request.
htmlSecurityPolicyHtmlSecurityPolicyInterface|nullnoDefaults to the core Hypertext Markup Language (HTML) security policy. Applies at the parse layer and complements the transport-layer policy.
responseFactoryPsr\Http\Message\ResponseFactoryInterface|nullno (default null)Required to activate the cURL-pinned transport. Without it the bridge always uses the injected PSR-18 client.
<?php
declare(strict_types=1);
use NextPDF\Gotenberg\GotenbergBridge;
$bridge = new GotenbergBridge(
config: $config,
httpClient: $psrHttpClient,
requestFactory: $psrRequestFactory,
streamFactory: $psrStreamFactory,
logger: $psrLogger,
responseFactory: $psrResponseFactory,
);

The bridge supports two transports and selects one for each conversion request:

  • cURL-pinned transport — used when a responseFactory was injected and there is something to pin (the URL resolved to one or more Internet Protocol (IP) addresses, or SPKI pins are configured). This transport binds the resolved address set with CURLOPT_RESOLVE. It enforces SPKI pinning with CURLOPT_PINNEDPUBLICKEY when pins are present. It verifies the peer and host (CURLOPT_SSL_VERIFYPEER, CURLOPT_SSL_VERIFYHOST = 2). It applies the configured timeout and disables redirect following (CURLOPT_FOLLOWLOCATION = false, CURLOPT_MAXREDIRS = 0).
  • Injected PSR-18 client — used in every other case, including when the application programming interface (API) URL is a bare public IP literal (no Domain Name System (DNS) to pin) and no SPKI pins are configured, or when no responseFactory was supplied.

For DNS-rebinding-resistant connections and TLS pinning, inject a responseFactory and configure pins. The health probe always uses the injected PSR-18 client, regardless of transport selection.

Pinning uses the Secure Hash Algorithm 256-bit (SHA-256) SPKI fingerprint model. Each pin is a string of the form sha256/<base64-encoded-spki-hash>. The transport also accepts the cURL-native sha256//<base64> form and converts the single-slash form to it. Any other prefix raises an InvalidSpkiPinException.

allPublicKeyPins() returns the de-duplicated union of pinnedPublicKeys and backupPublicKeys. The TLS layer accepts a certificate whose SPKI hash matches any member of that combined set. For operations, configure at least one backup pin so that a planned certificate or key rotation does not lock the bridge out of the service while the new key propagates. Keeping the backup list separate from the primary list lets you validate and rotate the backup pin independently of the active one. See /integrations/gotenberg/security-and-operations/ for the rotation procedure.

The multipart payload type (GotenbergConvertPayload) carries the file plus two optional Gotenberg conversion options:

  • landscape (bool, default false) — sent as the landscape form field with "true" or "false".
  • nativePageRanges (string, default '') — sent as a nativePageRanges form field only when non-empty; accepts Gotenberg’s range syntax, such as 1-3 or 1,3,5-9.

The public convertFile() and convertString() entry points build the payload with the defaults for both fields. The fields are part of the payload contract, and the test suite exercises them. Expose them from your integration layer if you need landscape output or page selection.

  • /integrations/gotenberg/install/ — installation and the Gotenberg baseline.
  • /integrations/gotenberg/quickstart/ — a runnable end-to-end example.
  • /integrations/gotenberg/production-usage/ — config sourcing, secrets, timeouts, retries.
  • /integrations/gotenberg/security-and-operations/ — the full security model and pin rotation.
  • /integrations/gotenberg/troubleshooting/ — configuration-related exception meanings.