Skip to content

Add the Craft Cloud asset transform driver - #213

Draft
timkelty wants to merge 3 commits into
5.xfrom
feature/craft-6-cloud-transforms
Draft

timkelty wants to merge 3 commits into
5.xfrom
feature/craft-6-cloud-transforms

Conversation

@timkelty

@timkelty timkelty commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

In 3.x, Cloud image transforms are Cloudflare Image Resizing URLs on the asset CDN. They're signed so the Cloud gateway can verify them. In Craft 6, a disk chooses its asset transformer with an asset_transformer key in config/filesystems.php (craftcms/cms#20542). This adds a craft-cloud transform driver for that. The URL signing scheme is unchanged, since the gateway expects it exactly.

Using it. Projects set the key on their Cloud disks:

'images' => [
    'driver' => 'craft-cloud',
    'asset_transformer' => 'craft-cloud',
],

A scoped disk without the key follows the disk it wraps. A Cloud disk without the key uses Craft's own transformer, which generates transforms and stores them in the bucket. Cloud doesn't add the key or change the default for you.

What gets transformed. The driver transforms assets whose volume disk is S3 in Cloud's bucket (CRAFT_CLOUD_PROJECT_ID). It checks the volume's own disk adapter rather than the disk's driver key, so scoped disks work. It handles:

  • Images it can handle: avif, gif, heic, heif, jpeg, jpg, png, svg and webp, matching the gateway's list.
  • PDFs. They default to format=auto, and the gateway rasterizes them.

Other file types throw NotSupportedException, so the CP shows file icons. GIFs and SVGs also throw when transformGifs or transformSvgs is off, as they do in core.

Everything else goes to the craft driver with only the core parameters, through transform(..., transformer: 'craft'). That covers craft-cloud disks in local development (where they're local disks), other S3 buckets, and temporary uploads, which aren't under the CDN's prefix. Because the driver is registered in every environment, the same filesystems.php works locally.

URLs. The base is the disk's CDN URL from AssetsHelper::generateUrl(), with the v rev parameter removed, as in 3.x. UrlSigner signs it the same way 3.x does: HTTP Message Signatures over @path and @query, in the s parameter, keyed by CRAFT_CLOUD_SIGNING_KEY. RequestSigner came along unchanged too; nothing uses it yet.

Parameters. CloudflareOptions ports the 3.x behavior mapping:

  • mode and upscale map to fit.
  • position, or the focal point when one is set, maps to gravity.
  • letterbox with fill maps to background.
  • jpg maps to baseline-jpeg or jpeg, depending on interlace.

The Cloudflare options are declared as parameter rules in definition(), so they're validated and can be used inline or on named transforms. Two differences from 3.x:

  • Invalid values now fail validation (core's InvalidAssetTransformException) instead of being dropped silently.
  • Craft's quality is now forwarded. 3.x ignored it, and the gateway passes it through to cf.image.

Dependencies. This needs craftcms/cms#20542, which hasn't merged. For now, craftcms/cms requires that PR's branch (dev-6.x-disk-owned-asset-transformers), with craftcms/cms-assets aliased to match, since core requires it at its own branch version. Switch both back to a tagged ^6.0 constraint once #20542 is released. The PR also adds craftcms/http-message-signatures, league/uri, league/uri-components and guzzlehttp/guzzle for signing.

Overlap with #212. Both PRs change composer.lock, so whichever merges second needs composer update --lock. The provider changes merge cleanly.

Not ported yet: 3.x's supportedImageFormats override, which the driver doesn't need, and the cloud.getImg() Twig helper.

Related issues

timkelty and others added 3 commits November 6, 2026 21:13
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant