Productiegebruik: fallback, telemetrie, archivering en bescherming
In één oogopslag
Sectie met titel “In één oogopslag”Deze pagina behandelt vier productieaspecten die verder gaan dan een eenvoudige render: lokale fallback, edge-telemetrie, archivering naar Cloudflare R2 en de beschermingslaag voor inkomende application programming interface (API)-verzoeken. Elke sectie sluit aan op gedrag dat in de klassen is geverifieerd.
Lokale fallback
Sectie met titel “Lokale fallback”Wanneer de Worker onbereikbaar is en fallbackToLocal op true staat, delegeert de bridge het renderen aan een lokale renderer. Lever die renderer aan via LocalRendererFactoryInterface. De bridge initialiseert die lazy, zodat create() van de factory alleen op het fallback-pad wordt uitgevoerd.
<?php
declare(strict_types=1);
use NextPDF\Cloudflare\Contract\LocalRendererFactoryInterface;use NextPDF\Cloudflare\Contract\LocalRendererInterface;
final class ArtisanLocalRendererFactory implements LocalRendererFactoryInterface{ public function __construct( private readonly \NextPDF\Artisan\ChromeHtmlRenderer $chrome, ) {}
public function create(): LocalRendererInterface { return new readonly class($this->chrome) implements LocalRendererInterface { public function __construct( private \NextPDF\Artisan\ChromeHtmlRenderer $chrome, ) {}
/** @param array<string, mixed> $options */ public function render(string $html, array $options = []): string { // Delegate to the local Chrome renderer; return raw PDF bytes. return $this->chrome->renderToString($html, $options); } }; }}Koppel de factory aan de renderer:
use NextPDF\Cloudflare\CloudflareHtmlRenderer;
$renderer = new CloudflareHtmlRenderer( config: $config, httpClient: $httpClient, requestFactory: $httpFactory, streamFactory: $httpFactory, logger: $logger, localRendererFactory: new ArtisanLocalRendererFactory($chrome), responseFactory: $httpFactory,);Wanneer fallback wordt uitgevoerd, is renderLocation van het resultaat de letterlijke tekenreeks local en is heightPt gelijk aan 0.0. Het lokale pad rapporteert geen edge-locatie of gemeten hoogte. De bridge geeft de gevraagde breedte via de optiesleutel widthPt door aan de lokale renderer.
Beslissingslogica voor fallback
Sectie met titel “Beslissingslogica voor fallback”Rechtstreeks uitgelezen uit CloudflareHtmlRenderer:
| Situatie | Uitkomst |
|---|---|
Configuratie onvolledig, fallbackToLocal: false | CloudflareNotAvailableException |
Configuratie onvolledig, fallbackToLocal: true, factory gekoppeld | Lokale render |
| Worker werpt een transportfout, fallback ingeschakeld, factory gekoppeld | Lokale render, gelogd op warning en vervolgens info |
| Worker werpt een fout, fallback ingeschakeld, Artisan geïnstalleerd, geen factory | CloudflareNotAvailableException die de ontbrekende factory benoemt |
| Worker werpt een fout, fallback ingeschakeld, Artisan niet geïnstalleerd | CloudflareNotAvailableException die het ontbrekende pakket benoemt |
| Worker retourneert een Hypertext Transfer Protocol (HTTP)-fout of een misvormde body | CloudflareRenderException, valt nooit terug |
De laatste rij is cruciaal. Een Worker die een fout retourneert, is een renderfout, geen bereikbaarheidsfout. De bridge geeft die opnieuw door, zodat je code een mislukte render kan onderscheiden van een onbereikbare edge.
Edge-telemetrie
Sectie met titel “Edge-telemetrie”Elke geslaagde render via het binaire pad bevat telemetrie uit de responseheaders:
$result = $renderer->render($html);
$logger->info('edge render', [ 'edge' => $result->renderLocation, // e.g. 'TPE', 'NRT' 'render_time_ms' => $result->renderTimeMs, 'content_px' => $result->contentHeightPx, 'pdf_bytes' => $result->size(),]);De renderer leest renderLocation uit de CF-Ray-responseheader en neemt het deel na het laatste koppelteken. Voor CF-Ray: 8abc123def456-TPE is de locatie TPE. Wanneer de header ontbreekt, is de locatie een lege tekenreeks. Op het JavaScript Object Notation (JSON)-responsepad komt de waarde in plaats daarvan uit het JSON-veld renderLocation. Beschouw deze waarden als observability-signalen van de Worker, niet als platformgaranties.
R2-archivering
Sectie met titel “R2-archivering”R2ArchiveManager uploadt Portable Document Format (PDF)-bytes naar Cloudflare R2 via de API die compatibel is met Amazon Simple Storage Service (S3) en ondertekent verzoeken met Amazon Web Services (AWS) Signature V4.
use NextPDF\Cloudflare\R2ArchiveConfig;use NextPDF\Cloudflare\R2ArchiveManager;
$r2 = new R2ArchiveManager( config: new R2ArchiveConfig( bucketName: 'pdf-archive', accountId: getenv('CF_ACCOUNT_ID') ?: '', accessKeyId: getenv('R2_ACCESS_KEY_ID') ?: '', secretAccessKey: getenv('R2_SECRET_ACCESS_KEY') ?: '', pathPrefix: 'invoices/', ), httpClient: $httpClient, requestFactory: $httpFactory, streamFactory: $httpFactory,);
$upload = $r2->upload($result->pdfData, 'invoice-2026-0042.pdf', [ 'tenant' => 'acme',]);
if (!$upload->success) { $logger->error('r2 upload failed', ['error' => $upload->error]);}Gedrag geverifieerd op basis van R2ArchiveManager en R2ObjectKey:
- De objectsleutel wordt per datum gepartitioneerd als:
<pathPrefix><Y>/<m>/<d>/<sanitized-filename>, bijvoorbeeldinvoices/2026/05/18/invoice-2026-0042.pdf. - De bestandsnaam wordt opgeschoond:
basename()verwijdert path traversal; daarna worden nullbytes en stuurtekens (\x00–\x1f,\x7f) verwijderd. Een leeg resultaat wordtdocument.pdf. - Aangepaste metadata wordt verzonden als
x-amz-meta-<lowercased-key>-headers, opgenomen in de set V4-ondertekende headers. - Bestanden die groter zijn dan
maxFileSizeBytes(standaard104857600) worden vóór elk verzoek afgewezen en retourneren eenR2UploadResultmetsuccess: false. R2UploadResult::isValid()vereistsuccess, een niet-legekeyen een niet-legeetag.
Vooraf ondertekende download-URL’s
Sectie met titel “Vooraf ondertekende download-URL’s”$url = $r2->generateSignedUrl('invoices/2026/05/18/invoice-2026-0042.pdf', 900);generateSignedUrl() bouwt een GET-URL die met een AWS Signature V4-query is ondertekend, met een X-Amz-Expires-waarde die je zelf bepaalt (standaard 3600 seconden). Het canonieke verzoek gebruikt de UNSIGNED-PAYLOAD-content-hash-sentinel. Een query-ondertekende lees-URL gebruikt deze vorm omdat de body geen onderdeel is van het ondertekende verzoek. Dit beschrijft het geïmplementeerde ondertekeningsgedrag van het pakket, zoals uitgelezen uit R2ArchiveManager. De servicedocumentatie van Amazon definieert AWS Signature Version 4, niet een standaard van een standards development organization (SDO), dus hier wordt geen normatieve clausule vastgelegd. Object-toegangssleutels zijn #[SensitiveParameter]; houd ze uit de logs.
Openbare URL’s
Sectie met titel “Openbare URL’s”R2UploadResult::publicUrl($customDomain) retourneert alleen de sleutel wanneer je geen domein opgeeft, of https://<domain>/<key> wanneer je dat wel doet. De methode voegt een Hypertext Transfer Protocol Secure (HTTPS)-schema toe wanneer het opgegeven domein er geen heeft. Dit maakt een privé-bucket niet openbaar; dat blijft een kwestie van de R2-bucketconfiguratie.
Inkomende API-bescherming
Sectie met titel “Inkomende API-bescherming”ApiProtection is de laag die je toepast op render-verzoeken die binnenkomen bij een PHP-gateway vóór de Worker. Deze controleert in een vaste volgorde: API-sleutel, dan payloadgrootte, dan rate limit.
use NextPDF\Cloudflare\ApiKeyValidator;use NextPDF\Cloudflare\ApiProtection;use NextPDF\Cloudflare\ApiProtectionConfig;
$protection = new ApiProtection( config: new ApiProtectionConfig( maxRequestsPerMinute: 30, maxRequestsPerHour: 500, maxPayloadSizeBytes: 5_000_000, requireApiKey: true, ), keyValidator: new ApiKeyValidator([getenv('GATEWAY_API_KEY') ?: '']),);
$decision = $protection->checkRequest( clientId: $clientIp, payloadSize: strlen($requestBody), apiKey: $request->getHeaderLine('X-Api-Key'),);
if (!$decision->allowed) { http_response_code(429); foreach ($decision->toHeaders() as $name => $value) { header("{$name}: {$value}"); } echo $decision->denialReason; exit;}Geverifieerd gedrag:
- De volgorde is API-sleutel → payloadgrootte → rate limit. De eerste mislukte controle breekt direct af met een specifieke
denialReason. ApiKeyValidator::validate()gebruikthash_equals()voor een timingveilige vergelijking en wijst een lege sleutel af.validateHashed()vergelijkt met Secure Hash Algorithm 256-bit (SHA-256)-hashes voor at-rest opslag van sleutels. Sleutelparameters dragen#[SensitiveParameter].- De rate-limit-opslag is in-memory per proces. Deze houdt een venster per minuut bij (
rateLimitWindowSeconds, standaard60) en een venster per uur (vast op3600seconden). De inhoud blijft niet behouden over workers of herstarts heen. Om limieten over processen heen te delen, plaats je er een gedeelde opslag voor. ApiProtectionResult::toHeaders()voegt altijdX-Content-Type-Options: nosniffenX-Frame-Options: DENYtoe en neemt de rate-limit-headers op (X-RateLimit-Remaining,X-RateLimit-Reset, plusRetry-Afterwanneer geweigerd).
Renderen en daarna ondertekenen
Sectie met titel “Renderen en daarna ondertekenen”Deze bridge ondertekent geen PDF’s. Om een productie-ondertekeningspijplijn te bouwen, render je aan de edge en onderteken je vervolgens de geretourneerde bytes met de engine:
render()→CloudflareRenderResult::$pdfData.- Geef
$pdfDatadoor aannextpdf/core(of NextPDF Pro voor ondertekening met PDF Advanced Electronic Signatures (PAdES) B-B). Long-term-validation-profielen zijn een Enterprise-functie; deze core-bridge claimt geen van deze functies.
Houd de ondertekeningsstap in je eigen proces, zodat de ondertekeningssleutel nooit de edge-grens passeert.
Zie ook
Sectie met titel “Zie ook”- /integrations/cloudflare/security-and-operations/ — pinning, verdediging tegen server-side request forgery (SSRF), rotatie van geheimen en het operationele runbook.
- /integrations/cloudflare/troubleshooting/ — catalogus van foutmodi.
- /integrations/cloudflare/configuration/ — elk veld en elke standaardwaarde.