Skip to content

Configure the NextPDF Cloudflare bridge

Three immutable configuration objects govern the package. Every default in this page is read from the corresponding constructor signature in src/Cloudflare/. It is not read from a specification or an estimate. Where this page states a maximum, that maximum is a limit this package enforces. It is not a statement about Cloudflare platform capacity.

The renderer’s configuration. final readonly. Construct it directly or with CloudflareRendererConfig::fromArray().

FieldTypeDefaultMeaning
workerUrlstring— (required)Worker endpoint URL. Must be HTTPS.
apiTokenstring— (required)Bearer token. Marked #[SensitiveParameter].
renderTimeoutint30Transfer timeout in seconds, applied by the pinned cURL transport.
defaultCssstring''CSS injected into the payload ahead of your HTML.
maxHtmlSizeint5000000Maximum HTML input size, in bytes, enforced before the request is sent.
r2FontBucket?stringnullR2 bucket name for custom font packages.
fallbackToLocalbooltrueWhether an unreachable Worker falls back to a local renderer.
pinnedPublicKeyslist<string>[]SHA-256 SPKI fingerprints, format sha256/<base64>.
backupPublicKeyslist<string>[]Backup SPKI pins, kept separate so rotation is validated independently.

isValid() returns true only when workerUrl !== '' and apiToken !== ''. allPublicKeyPins() returns the de-duplicated union of pinnedPublicKeys and backupPublicKeys. The TLS layer accepts a certificate whose SPKI hash appears in any member of that union. This matches RFC 7469 §2.6, which validates a pinned connection when the set of presented SPKI fingerprints intersects the pinned set. RFC 7469 §2.5 describes the backup pin as the primary recovery mechanism against inadvertent pin-validation failure. Keep at least one backup pin so a certificate rotation does not break the endpoint — see /integrations/cloudflare/security-and-operations/.

CloudflareRendererConfig::fromArray() reads snake_case keys and applies the same defaults if a key is absent or the wrong type:

Array keyMaps to
worker_urlworkerUrl
api_tokenapiToken
render_timeoutrenderTimeout (default 30)
default_cssdefaultCss
max_html_sizemaxHtmlSize (default 5000000)
r2_font_bucketr2FontBucket
fallback_to_localfallbackToLocal (default true)
pinned_public_keyspinnedPublicKeys (non-string members dropped)
backup_public_keysbackupPublicKeys (non-string members dropped)
<?php
declare(strict_types=1);
use NextPDF\Cloudflare\CloudflareRendererConfig;
$config = CloudflareRendererConfig::fromArray([
'worker_url' => 'https://pdf-renderer.example.workers.dev/render',
'api_token' => getenv('CF_PDF_TOKEN') ?: '',
'render_timeout' => 60,
'r2_font_bucket' => 'pdf-fonts',
'pinned_public_keys' => ['sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg='],
'backup_public_keys' => ['sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys='],
]);

These limits are enforced by CloudflareSecurityPolicy::validate() before any request leaves the process. The numbers are read from the source:

LimitValueWhere
Maximum HTML inputmaxHtmlSize (default 5000000 bytes)CloudflareSecurityPolicy::validate()
Maximum decoded base64 data-URI size13631488 bytes (≈13 MB)CloudflareSecurityPolicy::MAX_DATA_URI_BYTES

Exceeding either raises a RuntimeException with a message naming the offending size and the limit. The base64 ceiling is a decompression-bomb guard. The policy estimates the decoded size from the base64 length and rejects at or above the ceiling. A <meta http-equiv="refresh"> tag is also rejected, case-insensitively, because it can drive a redirect from inside the rendered page.

The package states only the limits it enforces itself. It makes no claim about the Worker’s own request, CPU, or memory ceilings. Consult Cloudflare’s official documentation and your Worker implementation for those.

Configuration for the optional request-protection layer that a Worker — or a PHP gateway in front of one — applies to inbound render requests. final readonly.

FieldTypeDefaultMeaning
maxRequestsPerMinuteint60Per-client per-minute request ceiling.
maxRequestsPerHourint1000Per-client per-hour request ceiling.
maxPayloadSizeBytesint10485760Maximum inbound payload (≈10 MB).
allowedOriginslist<string>[]CORS allowlist. Empty means no origin restriction is expressed here.
requireApiKeybooltrueWhether an API key is required.
apiKeyHeaderstring'X-Api-Key'Header carrying the API key.
rateLimitWindowSecondsint60Per-minute window length, in seconds.

fromArray() reads max_requests_per_minute, max_requests_per_hour, max_payload_size_bytes, allowed_origins, require_api_key, api_key_header, and rate_limit_window_seconds. isValid() requires every numeric field to be positive and apiKeyHeader to be non-empty.

Configuration for archiving rendered PDFs to Cloudflare R2 over the S3-compatible API. final readonly.

FieldTypeDefaultMeaning
bucketNamestring— (required)R2 bucket. Validated against the S3 naming rule.
accountIdstring— (required)Cloudflare account ID, used to build the default endpoint.
accessKeyIdstring— (required)R2 access key ID. #[SensitiveParameter].
secretAccessKeystring— (required)R2 secret access key. #[SensitiveParameter].
endpointstring''Custom S3 endpoint. Empty builds the default from accountId.
pathPrefixstring'pdfs/'Key prefix for uploaded objects.
maxFileSizeBytesint104857600Maximum upload size (≈100 MB), enforced before upload.

The constructor rejects a non-empty bucketName that does not match the S3-compatible rule. That rule is: 3–63 characters, lowercase alphanumerics and hyphens, starting and ending with an alphanumeric. A violation raises InvalidArgumentException. isValid() requires bucketName, accountId, accessKeyId, and secretAccessKey to be non-empty. When endpoint is empty, getEndpoint() returns https://<accountId>.r2.cloudflarestorage.com.

apiToken, accessKeyId, and secretAccessKey carry the #[SensitiveParameter] attribute, so PHP redacts them from stack traces. Supply them from environment variables or a secrets manager. Never commit them. Configuration objects are immutable, so a value set once cannot be mutated after construction.

  • /integrations/cloudflare/quickstart/ — apply this configuration in a first render.
  • /integrations/cloudflare/production-usage/ — fallback, R2 archival, and API protection wired together.
  • /integrations/cloudflare/security-and-operations/ — pinning, SSRF defense, and secret rotation.