Cloudflare API reference
At a glance
Section titled “At a glance”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.
Common tasks
Section titled “Common tasks”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.
Renderer
Section titled “Renderer”This table covers the core renderer surface. Use it when you construct config, build the renderer, or make render and reachability calls.
| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
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. | CloudflareRendererConfig | None 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. | CloudflareRendererConfig | None expected. | Use for framework-style config arrays. |
CloudflareRendererConfig::isValid() | none. | Requires a non-empty worker URL and API token. | bool | None 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. | CloudflareHtmlRenderer | Container 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. | CloudflareRenderResult | CloudflareNotAvailableException, CloudflareRenderException, validation failures. | Validates HTML size and worker URL before network input/output (I/O). |
CloudflareHtmlRenderer::getHtmlSecurityPolicy() | none. | Returns the configured parse-layer policy. | HtmlSecurityPolicyInterface | None expected. | Use alongside endpoint protection and Worker URL validation. |
CloudflareHtmlRenderer::isAvailable() | none. | Sends a HEAD request to the worker when config is valid. | bool | Returns false on errors. | Use for readiness checks, not as the only runtime guard. |
Renderer value objects and security
Section titled “Renderer value objects and security”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.
| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
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. | CloudflareRenderResult | None expected. | Usually returned by CloudflareResponseParser::parse(). |
CloudflareRenderResult::isValid() | none. | Checks for non-empty PDF bytes that begin with a PDF header. | bool | None expected. | Use before archiving or passing bytes to another layer. |
CloudflareRenderResult::size() | none. | Counts rendered PDF bytes. | int | None 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. | CloudflareRenderPayload | None expected. | Request payload value object. |
CloudflareRenderPayload::toJson() | none. | Serializes HTML, size, CSS, and font references as JavaScript Object Notation (JSON) for the Worker. | string | JSON 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. | CloudflareRenderResult | CloudflareRenderException 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. | void | Validation exception. | Keep untrusted input checks outside the Worker boundary. |
CloudflareSecurityPolicy::validateWorkerUrl(string $url) | Worker URL. | Parses and validates the destination. | array | Validation exception. | Blocks unsafe endpoint forms before network I/O. |
CloudflareSecurityPolicy::assertPinsStillValid(string $host, array $pinnedIps) | Host and pinned IP list. | Verifies expected DNS pins. | void | Validation exception when pins are stale or invalid. | Use during operational checks for pinned deployments. |
API protection
Section titled “API protection”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.
| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
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. | ApiProtection | None 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. | ApiProtectionResult | None expected. | Checks API key and size, then rate limits. |
ApiProtection::getRateLimit(string $clientId) | Client identifier. | Does not record a request. | RateLimitResult | None expected. | Use to add rate-limit headers. |
new ApiKeyValidator(array $validKeys = []) | List of valid plaintext keys. | An empty list rejects all keys. | ApiKeyValidator | None expected. | Store secrets outside code and hydrate through configuration. |
ApiKeyValidator::validate(string $key) | Raw key. | Compares against configured plaintext keys using timing-safe logic. | bool | None 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. | self | None expected. | Treat the returned instance as the updated validator. |
ApiKeyValidator::revokeKey(string $key) | Raw key. | Removes the matching hash from a new validator instance. | self | None expected. | Treat the returned instance as the updated validator. |
ApiKeyValidator::hashKey(string $key) | Raw key. | Produces the stored hash representation. | string | None 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. | bool | None 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. | ApiProtectionConfig | None 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. | ApiProtectionConfig | None expected. | Use for framework config hydration. |
ApiProtectionConfig::isValid() | none. | Requires positive limits and coherent size and window values. | bool | None 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. | ApiProtectionResult | None 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. | RateLimitResult | None 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. | RateLimitEntry | None expected. | In-memory tracking object. |
RateLimitEntry::increment() | none. | Increments the in-memory counter for one client/window. | void | None expected. | Low-level helper used by ApiProtection. |
RateLimitEntry::isExpired(int $windowSeconds) | Window length in seconds. | Compares against the current time. | bool | None expected. | Runtime expiration helper. |
RateLimitEntry::isExpiredAt(int $now, int $windowSeconds) | Clock value and window length. | Compares against the supplied clock value. | bool | None expected. | Deterministic test helper. |
RateLimitEntry::reset() | none. | Resets count and window start time. | void | None expected. | Used when a new window begins. |
R2 archive
Section titled “R2 archive”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.
| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
new R2ArchiveManager(R2ArchiveConfig $config, ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory) | R2 config and PSR HTTP factories/client. | No network call during construction. | R2ArchiveManager | Container 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. | R2UploadResult | Returns 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. | string | Signing errors from invalid config. | Use short TTLs for sensitive PDFs. |
R2ArchiveManager::buildObjectKey(string $filename) | Original filename. | Uses the configured path prefix and current date. | R2ObjectKey | None expected. | Use for predictable archive partitioning. |
R2ArchiveManager::createPutRequest(R2ObjectKey $key, string $data, array $metadata = []) | Object key, raw bytes, metadata. | Signs a PUT request. | RequestInterface | Request 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. | R2ArchiveConfig | InvalidArgumentException 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. | R2ArchiveConfig | Invalid bucket name when provided. | Use for application config hydration. |
R2ArchiveConfig::isValid() | none. | Requires non-empty account, bucket, access key, and secret key. | bool | None 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. | string | None expected. | Used for signed request construction. |
new R2ObjectKey(string $key, string $bucket) | Full object key and bucket. | No normalization. | R2ObjectKey | None 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. | R2ObjectKey | None expected. | Inject date in tests for deterministic keys. |
R2ObjectKey::fullPath() | none. | Joins the partition path and object filename. | string | None 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. | R2UploadResult | None expected. | Returned by R2ArchiveManager::upload(). |
R2UploadResult::isValid() | none. | Valid when upload succeeded and both key and ETag are present. | bool | None expected. | Check before exposing URLs. |
R2UploadResult::publicUrl(string $customDomain = '') | Optional custom public domain. | Returns the bare object key when no custom domain is supplied. | string | None expected. | Avoid public URLs for sensitive documents unless policy allows it. |
Transport helpers
Section titled “Transport helpers”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.
| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
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. | PinnedCurlTransport | None 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. | ResponseInterface | PSR-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(). | array | Invalid 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. | string | Implementation-specific render errors. | Used as local fallback when Worker rendering is unavailable. |
LocalRendererFactoryInterface::create() | none. | Creates a local renderer implementation. | LocalRendererInterface | Factory or dependency errors. | Keeps fallback renderer construction out of CloudflareHtmlRenderer. |
Development notes
Section titled “Development notes”- 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.