Skip to content

Cloudflare API reference

The NextPDF\Cloudflare package bridges edge rendering. Your PHP process keeps the Hypertext Markup Language (HTML), and a Cloudflare Worker runs the headless browser. The package exposes a Worker-backed HTML renderer (CloudflareHtmlRenderer) and its returned value objects, a request-protection layer for render endpoints (ApiProtection), an R2 archive service for rendered Portable Document Format (PDF) files (R2ArchiveManager), and pinned-transport helpers for Transport Layer Security (TLS) and Domain Name System (DNS) hardening. Configuration lives in three immutable objects (CloudflareRendererConfig, ApiProtectionConfig, R2ArchiveConfig).

To start, build a CloudflareRendererConfig, wire it into CloudflareHtmlRenderer, and call render(). That call sends your HTML to the Worker and returns a CloudflareRenderResult with the PDF bytes. Protection, archival, and pinning all layer around that call.

These snippets cover the workflows you are most likely to use. Each one is self-contained, verified against src/Cloudflare/, and reads secrets from the environment.

Render an HTML string to a PDF at the edge with the canonical call:

<?php
declare(strict_types=1);
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use NextPDF\Cloudflare\CloudflareHtmlRenderer;
use NextPDF\Cloudflare\CloudflareRendererConfig;
$httpFactory = new HttpFactory();
$renderer = new CloudflareHtmlRenderer(
config: new CloudflareRendererConfig(
workerUrl: 'https://pdf-renderer.example.workers.dev/render',
apiToken: getenv('CF_PDF_TOKEN') ?: throw new RuntimeException('CF_PDF_TOKEN not set'),
),
httpClient: new Client(),
requestFactory: $httpFactory,
streamFactory: $httpFactory,
responseFactory: $httpFactory,
);
$result = $renderer->render('<h1>Hello from the edge</h1>', widthPt: 595.28);
if ($result->isValid()) {
file_put_contents('output.pdf', $result->pdfData);
}

What it does: sends the HTML to the Worker over Hypertext Transfer Protocol Secure (HTTPS) and writes the returned A4 PDF bytes to disk after isValid() confirms a real PDF.

Archive a rendered PDF to R2 and return a short-lived link:

<?php
declare(strict_types=1);
use NextPDF\Cloudflare\R2ArchiveConfig;
use NextPDF\Cloudflare\R2ArchiveManager;
$archive = new R2ArchiveManager(
config: R2ArchiveConfig::fromArray([
'bucket_name' => 'pdf-archive',
'account_id' => getenv('CF_ACCOUNT_ID') ?: '',
'access_key_id' => getenv('R2_ACCESS_KEY_ID') ?: '',
'secret_access_key' => getenv('R2_SECRET_ACCESS_KEY') ?: '',
]),
httpClient: $httpClient, // PSR-18 ClientInterface
requestFactory: $requestFactory, // PSR-17 RequestFactoryInterface
streamFactory: $streamFactory, // PSR-17 StreamFactoryInterface
);
$upload = $archive->upload($result->pdfData, 'invoice-1234.pdf');
$signedUrl = $upload->isValid()
? $archive->generateSignedUrl($upload->key, expiresInSeconds: 600)
: null;

What it does: uploads the PDF bytes to a date-partitioned R2 key and, on success, creates a 10-minute pre-signed uniform resource locator (URL) for temporary download.

Guard a render endpoint before it starts expensive Worker work:

<?php
declare(strict_types=1);
use NextPDF\Cloudflare\ApiKeyValidator;
use NextPDF\Cloudflare\ApiProtection;
use NextPDF\Cloudflare\ApiProtectionConfig;
$protection = new ApiProtection(
config: new ApiProtectionConfig(maxRequestsPerMinute: 30),
keyValidator: new ApiKeyValidator([getenv('RENDER_API_KEY') ?: '']),
);
$decision = $protection->checkRequest(
clientId: $clientIp,
payloadSize: strlen($html),
apiKey: $presentedApiKey,
);
if (!$decision->allowed) {
// Reject with 429 and rate-limit headers before any render call.
return [429, $decision->toHeaders(), $decision->denialReason];
}

What it does: validates the API key and payload size, checks the per-client rate limit, and returns one decision plus the response headers to attach when the request is denied.

This table covers the core renderer surface. Use it when you construct config, build the renderer, or make render and reachability calls.

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
new CloudflareRendererConfig(string $workerUrl, string $apiToken, int $renderTimeout = 30, string $defaultCss = '', int $maxHtmlSize = 5000000, ?string $r2FontBucket = null, bool $fallbackToLocal = true, array $pinnedPublicKeys = [], array $backupPublicKeys = [])Worker URL, bearer token, timeout, Cascading Style Sheets (CSS), size limit, optional R2 font bucket, fallback flag, pin sets.Local fallback is enabled; pinning is off when pin arrays are empty.CloudflareRendererConfigNone expected.Keep the API token secret; prefer HTTPS worker URLs.
CloudflareRendererConfig::fromArray(array $config)worker_url, api_token, render_timeout, default_css, max_html_size, r2_font_bucket, fallback_to_local, pin arrays.Missing optional keys use constructor defaults.CloudflareRendererConfigNone expected.Use for framework-style config arrays.
CloudflareRendererConfig::isValid()none.Requires a non-empty worker URL and API token.boolNone expected.An invalid config triggers fallback or failure in the renderer.
CloudflareRendererConfig::allPublicKeyPins()none.Combines primary and backup public-key pins.list<string>None expected.An empty list disables pinning.
new CloudflareHtmlRenderer(CloudflareRendererConfig $config, ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, ?LoggerInterface $logger = null, ?LocalRendererFactoryInterface $localRendererFactory = null, ?HtmlSecurityPolicyInterface $htmlSecurityPolicy = null, ?ResponseFactoryInterface $responseFactory = null)Config, PHP Standards Recommendation (PSR) Hypertext Transfer Protocol (HTTP) dependencies, optional logger, optional local fallback factory, optional HTML policy, optional response factory.Uses DefaultHtmlSecurityPolicy when no HTML policy is supplied.CloudflareHtmlRendererContainer wiring errors.The response factory enables pinned cURL transport when needed.
CloudflareHtmlRenderer::render(string $html, float $widthPt = 595.28, float $heightPt = 0, array $fontFiles = [])HTML, page width, page height, font files in R2.A4 width, automatic height, no font files.CloudflareRenderResultCloudflareNotAvailableException, CloudflareRenderException, validation failures.Validates HTML size and worker URL before network input/output (I/O).
CloudflareHtmlRenderer::getHtmlSecurityPolicy()none.Returns the configured parse-layer policy.HtmlSecurityPolicyInterfaceNone expected.Use alongside endpoint protection and Worker URL validation.
CloudflareHtmlRenderer::isAvailable()none.Sends a HEAD request to the worker when config is valid.boolReturns false on errors.Use for readiness checks, not as the only runtime guard.

Use this table for request and result value objects (CloudflareRenderResult, CloudflareRenderPayload) and static transport-layer checks that validate HTML, the Worker URL, and DNS pins before network I/O.

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
new CloudflareRenderResult(string $pdfData, float $widthPt, float $heightPt, float $contentHeightPx = 0.0, string $renderLocation = '', float $renderTimeMs = 0.0)PDF bytes, width, height, measured content height, edge location, render time.Metadata is empty when the Worker does not report it.CloudflareRenderResultNone expected.Usually returned by CloudflareResponseParser::parse().
CloudflareRenderResult::isValid()none.Checks for non-empty PDF bytes that begin with a PDF header.boolNone expected.Use before archiving or passing bytes to another layer.
CloudflareRenderResult::size()none.Counts rendered PDF bytes.intNone expected.Feed into quota and audit logic.
new CloudflareRenderPayload(string $html, float $widthPt, float $heightPt = 0, string $defaultCss = '', ?string $r2FontBucket = null, array $fontFiles = [])HTML, size, CSS, optional R2 font bucket, font file list.Automatic height, no default CSS, no R2 font bucket, no font files.CloudflareRenderPayloadNone expected.Request payload value object.
CloudflareRenderPayload::toJson()none.Serializes HTML, size, CSS, and font references as JavaScript Object Notation (JSON) for the Worker.stringJSON encoding errors.Low-level request payload API.
CloudflareResponseParser::parse(ResponseInterface $response, float $requestedWidthPt)Worker response and requested width.Accepts binary PDF responses and structured JSON responses.CloudflareRenderResultCloudflareRenderException for failed or invalid Worker output.Central parser used by the renderer.
CloudflareSecurityPolicy::validate(string $html, int $maxSize)HTML input and size limit.Applies the package HTML input policy.voidValidation exception.Keep untrusted input checks outside the Worker boundary.
CloudflareSecurityPolicy::validateWorkerUrl(string $url)Worker URL.Parses and validates the destination.arrayValidation exception.Blocks unsafe endpoint forms before network I/O.
CloudflareSecurityPolicy::assertPinsStillValid(string $host, array $pinnedIps)Host and pinned IP list.Verifies expected DNS pins.voidValidation exception when pins are stale or invalid.Use during operational checks for pinned deployments.

Use this table when you guard a render endpoint: API-key validation, payload-size and rate-limit checks, and the result and header objects they produce.

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
new ApiProtection(ApiProtectionConfig $config, ?ApiKeyValidator $keyValidator = null, ?Closure $clock = null)Protection config, optional key validator, optional clock.Uses system time when no clock is supplied.ApiProtectionNone expected.Inject a deterministic clock in tests.
ApiProtection::checkRequest(string $clientId, int $payloadSize, string $apiKey = '')Client identifier, payload size, optional API key.Empty API key is allowed only when config does not require keys.ApiProtectionResultNone expected.Checks API key and size, then rate limits.
ApiProtection::getRateLimit(string $clientId)Client identifier.Does not record a request.RateLimitResultNone expected.Use to add rate-limit headers.
new ApiKeyValidator(array $validKeys = [])List of valid plaintext keys.An empty list rejects all keys.ApiKeyValidatorNone expected.Store secrets outside code and hydrate through configuration.
ApiKeyValidator::validate(string $key)Raw key.Compares against configured plaintext keys using timing-safe logic.boolNone expected.The parameter is sensitive; do not log raw keys.
ApiKeyValidator::addKey(string $key)Raw key.Adds a hashed key to a new validator instance.selfNone expected.Treat the returned instance as the updated validator.
ApiKeyValidator::revokeKey(string $key)Raw key.Removes the matching hash from a new validator instance.selfNone expected.Treat the returned instance as the updated validator.
ApiKeyValidator::hashKey(string $key)Raw key.Produces the stored hash representation.stringNone expected.Do not expose hashes in logs or client responses.
ApiKeyValidator::validateHashed(string $key, array $hashedKeys)Raw key and candidate hashes.Compares against supplied hashes in constant time.boolNone expected.Low-level helper for custom key stores.
new ApiProtectionConfig(int $maxRequestsPerMinute = 60, int $maxRequestsPerHour = 1000, int $maxPayloadSizeBytes = 10485760, array $allowedOrigins = [], bool $requireApiKey = true, string $apiKeyHeader = 'X-Api-Key', int $rateLimitWindowSeconds = 60)Request limits, payload limit, allowed origins, API-key requirement, header name, window length.60/minute, 1000/hour, 10 MiB payload, API key required.ApiProtectionConfigNone expected.Construct directly in tests or hydrate with fromArray().
ApiProtectionConfig::fromArray(array $data)max_requests_per_minute, max_requests_per_hour, max_payload_size_bytes, allowed_origins, require_api_key, api_key_header, rate_limit_window_seconds.Missing keys use constructor defaults.ApiProtectionConfigNone expected.Use for framework config hydration.
ApiProtectionConfig::isValid()none.Requires positive limits and coherent size and window values.boolNone expected.Validate before exposing an endpoint.
new ApiProtectionResult(bool $allowed, string $denialReason = '', ?RateLimitResult $rateLimit = null)Decision, denial reason, optional rate-limit result.Empty denial reason and no rate-limit result.ApiProtectionResultNone expected.Returned by ApiProtection::checkRequest().
ApiProtectionResult::toHeaders()none.Emits rate-limit headers when rate data exists.array<string, string>None expected.Attach to Worker or framework responses.
new RateLimitResult(bool $allowed, int $remainingRequests, int $retryAfterSeconds, string $clientId)Decision, remaining count, retry delay, client identifier (ID).No default values.RateLimitResultNone expected.Immutable result for one check.
RateLimitResult::toHeaders()none.Emits remaining-limit and reset headers.array<string, string>None expected.Use for observability and client backoff.
new RateLimitEntry(string $clientId, int $requestCount = 0, int $windowStart = 0, int $hourlyCount = 0, int $hourlyWindowStart = 0)Client ID and mutable counters.Counters start at zero.RateLimitEntryNone expected.In-memory tracking object.
RateLimitEntry::increment()none.Increments the in-memory counter for one client/window.voidNone expected.Low-level helper used by ApiProtection.
RateLimitEntry::isExpired(int $windowSeconds)Window length in seconds.Compares against the current time.boolNone expected.Runtime expiration helper.
RateLimitEntry::isExpiredAt(int $now, int $windowSeconds)Clock value and window length.Compares against the supplied clock value.boolNone expected.Deterministic test helper.
RateLimitEntry::reset()none.Resets count and window start time.voidNone expected.Used when a new window begins.

Use this table when you store rendered PDFs in Cloudflare R2: the archive service, its config and object-key types, and the upload result to inspect before you expose a URL.

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
new R2ArchiveManager(R2ArchiveConfig $config, ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory)R2 config and PSR HTTP factories/client.No network call during construction.R2ArchiveManagerContainer wiring errors.Main archive service.
R2ArchiveManager::upload(string $pdfData, string $filename, array $metadata = [])Raw PDF bytes, original filename, string metadata.Empty metadata; date-partitioned key.R2UploadResultReturns unsuccessful result on config, size, HTTP, or transport failure.Does not throw for normal upload failure.
R2ArchiveManager::generateSignedUrl(string $key, int $expiresInSeconds = 3600)Object key and URL time to live (TTL).One-hour signed URL.stringSigning errors from invalid config.Use short TTLs for sensitive PDFs.
R2ArchiveManager::buildObjectKey(string $filename)Original filename.Uses the configured path prefix and current date.R2ObjectKeyNone expected.Use for predictable archive partitioning.
R2ArchiveManager::createPutRequest(R2ObjectKey $key, string $data, array $metadata = [])Object key, raw bytes, metadata.Signs a PUT request.RequestInterfaceRequest construction errors.Low-level API for custom transports.
new R2ArchiveConfig(string $bucketName, string $accountId, string $accessKeyId, string $secretAccessKey, string $endpoint = '', string $pathPrefix = 'pdfs/', int $maxFileSizeBytes = 104857600)Bucket, account ID, credentials, endpoint override, key prefix, maximum object size.Derived endpoint, pdfs/ prefix, 100 MiB maximum object size.R2ArchiveConfigInvalidArgumentException for invalid bucket names.Treat credentials as secret-bearing config.
R2ArchiveConfig::fromArray(array $data)Account ID, bucket, credentials, path prefix, endpoint override, maximum size.Missing values use constructor defaults.R2ArchiveConfigInvalid bucket name when provided.Use for application config hydration.
R2ArchiveConfig::isValid()none.Requires non-empty account, bucket, access key, and secret key.boolNone expected.Invalid config makes uploads fail with structured results.
R2ArchiveConfig::getEndpoint()none.Uses an explicit endpoint or derives the Cloudflare R2 endpoint from account ID.stringNone expected.Used for signed request construction.
new R2ObjectKey(string $key, string $bucket)Full object key and bucket.No normalization.R2ObjectKeyNone expected.Usually created by R2ObjectKey::generate().
R2ObjectKey::generate(string $prefix, string $filename, ?DateTimeInterface $date = null)Prefix, original filename, optional date.Date-partitioned, sanitized object key.R2ObjectKeyNone expected.Inject date in tests for deterministic keys.
R2ObjectKey::fullPath()none.Joins the partition path and object filename.stringNone expected.Store this value as the object key.
new R2UploadResult(bool $success, string $key, string $etag = '', int $size = 0, string $error = '')Success flag, object key, entity tag (ETag), byte size, error message.Empty ETag, zero size, empty error.R2UploadResultNone expected.Returned by R2ArchiveManager::upload().
R2UploadResult::isValid()none.Valid when upload succeeded and both key and ETag are present.boolNone expected.Check before exposing URLs.
R2UploadResult::publicUrl(string $customDomain = '')Optional custom public domain.Returns the bare object key when no custom domain is supplied.stringNone expected.Avoid public URLs for sensitive documents unless policy allows it.

Use this table only for low-level wiring: cURL-level Internet Protocol (IP) and SubjectPublicKeyInfo (SPKI) pinning, plus the local-renderer contracts used as the fallback path when the Worker is unreachable.

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
new PinnedCurlTransport(ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory, array $pinnedIps = [], array $pinnedPublicKeys = [], int $timeoutSeconds = 30)PSR-17 factories, pinned IPs, pinned public keys, timeout.No pins and a 30-second timeout.PinnedCurlTransportNone expected.Use only when cURL-level pinning is required.
PinnedCurlTransport::sendRequest(RequestInterface $request)PSR-7 request.Sends through cURL with the configured timeout and pinning controls.ResponseInterfacePSR-18 transport exceptions.Use only when framework HTTP clients cannot enforce the same pinning policy.
PinnedCurlTransport::buildCurlOptions(RequestInterface $request, string $host, int $port)Request, target host, target port.Builds the cURL option array used by sendRequest().arrayInvalid request or pin configuration errors.Low-level test and diagnostics hook.
LocalRendererInterface::render(string $html, array $options = [])HTML and renderer options.Contract only; implementation sets defaults.stringImplementation-specific render errors.Used as local fallback when Worker rendering is unavailable.
LocalRendererFactoryInterface::create()none.Creates a local renderer implementation.LocalRendererInterfaceFactory or dependency errors.Keeps fallback renderer construction out of CloudflareHtmlRenderer.
  • Treat the Worker URL as a network boundary. Validate destination, size, and authentication before rendering.
  • Use API-protection results as policy outputs, not exception control flow.
  • R2 uploads return structured success or error results; handle both paths.