Configure the NextPDF Cloudflare bridge
At a glance
Section titled “At a glance”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.
CloudflareRendererConfig
Section titled “CloudflareRendererConfig”The renderer’s configuration. final readonly. Construct it directly or
with CloudflareRendererConfig::fromArray().
| Field | Type | Default | Meaning |
|---|---|---|---|
workerUrl | string | — (required) | Worker endpoint URL. Must be HTTPS. |
apiToken | string | — (required) | Bearer token. Marked #[SensitiveParameter]. |
renderTimeout | int | 30 | Transfer timeout in seconds, applied by the pinned cURL transport. |
defaultCss | string | '' | CSS injected into the payload ahead of your HTML. |
maxHtmlSize | int | 5000000 | Maximum HTML input size, in bytes, enforced before the request is sent. |
r2FontBucket | ?string | null | R2 bucket name for custom font packages. |
fallbackToLocal | bool | true | Whether an unreachable Worker falls back to a local renderer. |
pinnedPublicKeys | list<string> | [] | SHA-256 SPKI fingerprints, format sha256/<base64>. |
backupPublicKeys | list<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/.
fromArray() key map
Section titled “fromArray() key map”CloudflareRendererConfig::fromArray() reads snake_case keys and
applies the same defaults if a key is absent or the wrong type:
| Array key | Maps to |
|---|---|
worker_url | workerUrl |
api_token | apiToken |
render_timeout | renderTimeout (default 30) |
default_css | defaultCss |
max_html_size | maxHtmlSize (default 5000000) |
r2_font_bucket | r2FontBucket |
fallback_to_local | fallbackToLocal (default true) |
pinned_public_keys | pinnedPublicKeys (non-string members dropped) |
backup_public_keys | backupPublicKeys (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='],]);Input-size limits the package enforces
Section titled “Input-size limits the package enforces”These limits are enforced by CloudflareSecurityPolicy::validate()
before any request leaves the process. The numbers are read from the
source:
| Limit | Value | Where |
|---|---|---|
| Maximum HTML input | maxHtmlSize (default 5000000 bytes) | CloudflareSecurityPolicy::validate() |
| Maximum decoded base64 data-URI size | 13631488 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.
ApiProtectionConfig
Section titled “ApiProtectionConfig”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.
| Field | Type | Default | Meaning |
|---|---|---|---|
maxRequestsPerMinute | int | 60 | Per-client per-minute request ceiling. |
maxRequestsPerHour | int | 1000 | Per-client per-hour request ceiling. |
maxPayloadSizeBytes | int | 10485760 | Maximum inbound payload (≈10 MB). |
allowedOrigins | list<string> | [] | CORS allowlist. Empty means no origin restriction is expressed here. |
requireApiKey | bool | true | Whether an API key is required. |
apiKeyHeader | string | 'X-Api-Key' | Header carrying the API key. |
rateLimitWindowSeconds | int | 60 | Per-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.
R2ArchiveConfig
Section titled “R2ArchiveConfig”Configuration for archiving rendered PDFs to Cloudflare R2 over the
S3-compatible API. final readonly.
| Field | Type | Default | Meaning |
|---|---|---|---|
bucketName | string | — (required) | R2 bucket. Validated against the S3 naming rule. |
accountId | string | — (required) | Cloudflare account ID, used to build the default endpoint. |
accessKeyId | string | — (required) | R2 access key ID. #[SensitiveParameter]. |
secretAccessKey | string | — (required) | R2 secret access key. #[SensitiveParameter]. |
endpoint | string | '' | Custom S3 endpoint. Empty builds the default from accountId. |
pathPrefix | string | 'pdfs/' | Key prefix for uploaded objects. |
maxFileSizeBytes | int | 104857600 | Maximum 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.
Secret handling
Section titled “Secret handling”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.
See also
Section titled “See also”- /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.