Repository navigation
Conversation
Ports 3.x's signed Cloudflare transform URLs to Craft 6's asset transform driver registry, and registers a craftCloud transformer profile that becomes the default on Cloud. Amp-Thread-ID: https://ampcode.com/threads/T-01a1b2a5-ffcf-7312-966f-7e8624cf8b79 Co-authored-by: Amp <amp@ampcode.com>
Core now picks transformers with a disk's asset_transformer key instead of registered profiles (craftcms/cms#20542). Register the craft-cloud driver with AssetTransformers, drop the craftCloud profile and the defaultAssetTransformer switch, and detect Cloud storage from the asset's own disk, so scoped disks work. Amp-Thread-ID: https://ampcode.com/threads/T-01a1b2a5-ffcf-7312-966f-7e8624cf8b79 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1b2a5-ffcf-7312-966f-7e8624cf8b79 Co-authored-by: Amp <amp@ampcode.com>
#20542 merged without the per-call transformer argument, so hand files that aren't in Cloud's bucket straight to the craft driver. Drop the branch pin and lock to 6.x at 8a13417. Amp-Thread-ID: https://ampcode.com/threads/T-01a1b2a5-ffcf-7312-966f-7e8624cf8b79 Co-authored-by: Amp <amp@ampcode.com>
This was referenced Nov 10, 2026
Design the driver on core's contract instead of porting 3.x: map core parameters onto Cloudflare options, declare only the Cloudflare-only ones, report output dimensions, and use the focal point only for crops, like the craft driver. Sign with http-message-signatures directly, without 3.x's query normalization, and add the asset's revision after signing, since the gateway leaves v out of signatures. Trust each disk's asset_transformer choice instead of checking the bucket, and hand only non-S3 disks to the craft driver. Require craftcms/cms ^6.0.0-beta.1. Amp-Thread-ID: https://ampcode.com/threads/T-01a1b2a5-ffcf-7312-966f-7e8624cf8b79 Co-authored-by: Amp <amp@ampcode.com>
timkelty
added a commit
that referenced
this pull request
Nov 10, 2026
Tag pages with their Craft cache tags and derive CDN-Cache-Control from the final response headers. Purge on every TagDependency invalidation, except drafts and revisions, with one queued PurgeStaticCache job per request, command or queue job, signed with #213's RequestSigner. Add a Clear Caches option and a single cloud:purge command. Require craftcms/cms ^6.0.0-beta.1. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-6551416a-ce7c-4e90-adac-8c2edc72248c
timkelty
added a commit
that referenced
this pull request
Nov 10, 2026
Tag pages with their Craft cache tags and derive CDN-Cache-Control from the final response headers. Purge on every TagDependency invalidation, except drafts and revisions, with one queued PurgeStaticCache job per request, command or queue job, signed with #213's RequestSigner. Add a Clear Caches option and a single cloud:purge command. Require craftcms/cms ^6.0.0-beta.1. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-6551416a-ce7c-4e90-adac-8c2edc72248c
timkelty
added a commit
that referenced
this pull request
Nov 10, 2026
Tag pages with their Craft cache tags and derive CDN-Cache-Control from the final response headers. Purge on every TagDependency invalidation, except drafts and revisions, with one queued PurgeStaticCache job per request, command or queue job, signed with #213's RequestSigner. Add a Clear Caches option and a single cloud:purge command. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-6551416a-ce7c-4e90-adac-8c2edc72248c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Craft 6 lets each disk choose an asset transformer with an
asset_transformerkey inconfig/filesystems.php. This adds acraft-cloudtransform driver. It turns transforms into signed Cloudflare image URLs on Craft Cloud's asset CDN, and the Cloud gateway verifies those URLs before transforming. It's built on core'sAssetTransformDrivercontract rather than ported from 3.x. The only parts kept from 3.x are the external contracts: the URL format and signature the gateway verifies, and the Cloudflare options it accepts.Using it. Add the key to the Cloud disks whose assets should be transformed on the CDN. Cloud doesn't add it for you:
A scoped disk without the key follows the disk it wraps. A disk without the key uses Craft's own
craftdriver.Which assets it transforms. The driver trusts the disk's choice. It transforms images (
avif,gif,heic,heif,jpeg,jpg,png,svg,webp) and PDFs on any S3 disk that chooses it. Other file types throwNotSupportedException, so the CP shows file icons. So do GIFs and SVGs whentransformGifsortransformSvgsis off.Assets on disks that aren't S3 go to the
craftdriver with only the core parameters. Core can't hand a transform to another driver, so the driver callsAssetTransformers::driver('craft')directly. That covers a disk shared between environments that islocalin development, the setup #212 documents.An earlier revision also checked that the disk was in Cloud's bucket and wasn't the temporary-upload disk. I removed both checks:
craft-clouddisk driver from Register Craft Cloud storage as Laravel disks #212 only ever builds S3 disks on the project's bucket, with CDN URLs. A bucket check can't tell a dev bucket from Cloud's, since both come fromCRAFT_CLOUD_PROJECT_ID.asset_transformer, so temporary uploads only reach this driver if a project puts them on a disk that chose it.Parameters.
CloudflareOptionsmaps core parameters onto Cloudflare's options:width,heightandqualitypass through.modeandupscalemap tofit. Withoutupscale, Craft'supscaleImagessetting applies.positionmaps togravity. Crops use the asset's focal point instead, as thecraftdriver does.fillmaps tobackgroundfor letterboxing.jpgmaps tobaseline-jpeg, or tojpegwhen interlaced.Cloudflare options with no core equivalent are declared as
parameterRules, so they're validated, and can be passed inline or set on named transforms undercraft-cloud. They areanim,background,blur,border,brightness,compression,contrast,dpr,draw,flip,gamma,gravity,metadata,rotate,saturation,segment,sharpen,trimandzoom, plus the gateway's PDFpage. An explicitgravityorbackgroundwins over the derived one. Cloudflare's ownfitisn't declared, sincemodeandupscalecover all six values. There are no CP fields for these parameters, and undeclared parameters aren't forwarded.Results. Each result reports its output width and height, worked out the same way the
craftdriver does it, becausegetImg(),getWidth($transform)andxsrcset sizes read them. The MIME type comes from the requested format, or from the source when the gateway negotiates the format.PDFs. PDFs default to
format=auto. The gateway only rasterizes PDFs when a format is requested, andautolets it pick AVIF or WebP. Without either, it falls back to PNG, which is the MIME type reported.URLs and signing.
AssetCdnbuilds the URL: the asset's URL with the options in its query, signed with HTTP Message Signatures over@pathand@query, in thesparameter, keyed byCRAFT_CLOUD_SIGNING_KEY. It uses craftcms/http-message-signatures directly. 3.x'sUrlSignerwrapper, its query normalization for older Craft URL encoding, and itsverify()were only needed for 3.x's ESI URLs, so they're gone.The gateway leaves
vout of signatures, so the asset's revision is now added after signing whenrevAssetUrlsis on, instead of being stripped. Transform URLs change when the file or focal point does. A test confirms that a URL still verifies after core'sgetThumbUrl()re-serializes it to addv.RequestSigneris unchanged and stays shared for #214.Possible gateway change. If the gateway rasterized PDFs without a requested format, defaulting to
autoas it does for images, the driver's PDF special case would go away. I haven't found any other contract change that would make this meaningfully simpler.Dependencies. This requires
craftcms/cms^6.0.0-beta.1. Packagist hasn't indexedcraftcms/cms-assets6.0.0-beta.1 yet, and core requires it at its own version, so the lock currently resolves to6.x-devat 05ce9b8. That's the commit the beta.1 tag points to. Once Packagist catches up,composer update craftcms/cms craftcms/cms-assetsmoves both to the tag. This PR also addscraftcms/http-message-signatures,league/uri,league/uri-componentsandguzzlehttp/guzzle.Overlap with #212. Both PRs change
composer.lock, so whichever merges second needscomposer update --lock. The provider changes merge cleanly.Related issues