Repository navigation
Conversation
timkelty
force-pushed
the
cache-control-model
branch
from
November 10, 2026 18:22
fdf8c71 to
c0d2bac
Compare
timkelty
changed the base branch from
5.x
to
feature/craft-6-cloud-transforms
November 10, 2026 18:22
timkelty
force-pushed
the
cache-control-model
branch
from
November 10, 2026 18:24
c0d2bac to
5f931b2
Compare
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
timkelty
force-pushed
the
cache-control-model
branch
from
November 10, 2026 18:25
5f931b2 to
2f2afa4
Compare
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 Cloud's gateway and CDN cache site pages. Craft has to tell them how long each page may be cached and which content changes should purge it. This adds that, built on Craft 6's existing cache APIs. It's designed from scratch rather than ported from 3.x.
Stacked on #213 for its
RequestSigner. GitHub retargets it to5.xwhen #213 merges.Caching pages. The
CacheResponsemiddleware is prepended to Craft'scraftgroup and goes in Laravel's priority list beforeEncryptCookies. That puts it outside core'sSetHeadersand the cookie and session middleware, so it sees the final response. For successful GET and HEAD site responses it:ElementCaches::startCollectingCacheInfo()/stopCollectingCacheInfo(), including the matched elementCDN-Cache-Controlfrom the response's ownCache-Control:s-maxage, thenmax-ageorExpires. So{% expires %},ResponseHeaders::setCache()and Symfony'ssetSharedMaxAge()control the CDN, and the browser header is left alone. Without a lifetime, a page is cached for a year with an hour ofstale-while-revalidate. Lifetimes are capped at element expiry.private,no-cacheandno-storegiveno-store, and an explicitCDN-Cache-Controlheader is left as it is.Cache-Tagto the page's Craft tags, each hashed and prefixed with the first 8 characters of the environment ID. If they don't fit within Cloudflare's 1,000 tags or 16 KiB, it adds{env}:overflow.A response gets
no-storewhen:AuthorizationheaderHAD_TOKEN_KEY,HAD_SITE_TOKEN_KEY). Core only marks preview token responses as no-cache.Purging. Cloud listens for
TagsInvalidatedfrom craftcms/laravel-dependency-aware-cache, which fires for everyTagDependency::invalidate(). So the CDN follows Craft's own tag invalidation:element::Entry::section:Ntag invalidated when an entry is moved to another sectionElementCaches::collectCacheTags()craft:invalidate-tagsBatches with draft or revision tags are skipped, since every autosave invalidates
element::{class}::*. Core's internalelement-index-query::*andelement-search-query::*tags are filtered out as well.Tags are collected until the request, command or queue job is done, then sent as one queued
PurgeStaticCachejob. The SQS connection'safter_commitsends it after the save commits, failures are retried, and a slow gateway doesn't delay the response. On Lambda, the app terminates before the response is returned. The Caches utility gets a “Craft Cloud static cache” option, which core also exposes ascraft:clear-caches:cloud-static-cache.cloud:purgepurges everything, or specific tags (--tag) or URL prefixes (--prefix). Both run the job synchronously.What changed from 3.x, and why
StaticCacheTagand its normalization are gone. There's one function: an 8-character environment prefix plus hex CRC32. It's kept because Cloudflare tags are zone-wide and limited to 16 KiB per response, and a hash collision only causes an extra purge. The response's ownCache-Tagis no longer merged in, since custom tags go throughElementCaches::collectCacheTags().{env}:overflowis now purged with every invalidation. In 3.x, pages that dropped tags were only purged by a full purge.Cache-Purge-Tag: dropped. That header only works for web requests, so 3.x needed both it and the API. This uses only the gateway API.fetchUrls: dropped. A page is tagged with its matched element, and saving or deleting that element invalidateselement::{id}, so tag purges already cover it.fetchUrlsonly warmed the saved element's own URL. The 3.xDev-Modeheader is dropped too.cloud/static-cache/purge-*commands become onecloud:purge, plus the Caches utility option.config('cloud.*'). Durations are no longer configurable, since sites set them per response with Craft's own APIs. The package reads theCRAFT_CLOUD_ENVIRONMENT_ID,CRAFT_CLOUD_SIGNING_KEYandCRAFT_CLOUD_GATEWAY_BASE_URLenv vars Cloud already sets, like Register Craft Cloud storage as Laravel disks #212 and Add the Craft Cloud asset transform driver #213.Contract questions for infra. None of these needs a change:
{env}:overflowwith every tag purge, the line that adds it here can go.JobSqsHandler(an open plan item), queued purges won't run on Cloud. Onlycloud:purgeand the Caches utility work until then.cloud/static-cache/*commands, it needs to runcloud:purgeinstead.Dependencies. None. This doesn't change
composer.jsonor the lock. #213 already requirescraftcms/cms^6.0.0-beta.1andcraftcms/http-message-signatures. The lock there has cms at6.x-dev05ce9b8, the beta.1 commit, because Packagist hasn't indexedcraftcms/cms-assets6.0.0-beta.1yet, and cms beta.1 requires it atself.version.CloudServiceProvidergets a single line.Related issues