cors / pimcore-cdn
CORS - CDN providers for Pimcore's CDN integration (Cloudflare, Google Cloud CDN, AWS CloudFront, Azure Front Door)
Requires
- php: >=8.3
- ext-json: *
- guzzlehttp/guzzle: ^7.0
- pimcore/pimcore: ^2026.2
- psr/log: ^3.0
Requires (Dev)
- aws/aws-sdk-php: ^3.300
- coreshop/test-setup: dev-main
- google/auth: ^1.40
- phpunit/phpunit: ^11.0
- pimcore/studio-backend-bundle: ^2026.2
- pimcore/studio-ui-bundle: ^2026.2
- symfony/dotenv: ^8.0
- symfony/runtime: ^8.1
Suggests
- aws/aws-sdk-php: Required for the aws provider (CloudFront invalidations).
- google/auth: Required for the google provider (Cloud CDN invalidation via Application Default Credentials).
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-25 18:55:13 UTC
README
Provider implementations for Pimcore's built-in CDN integration (Pimcore ≥ 2026.2):
Cloudflare, Google Cloud CDN, AWS CloudFront and Azure Front Door in one
bundle. Pimcore emits Surrogate-Key / Cache-Tag headers on thumbnail responses and
dispatches purge messages on asset and thumbnail-config changes; this bundle supplies the
Pimcore\Cdn\PurgeClientInterface implementations (and, for Cloudflare, an
ImageTransformAdapterInterface) that talk to the provider APIs.
See Pimcore's doc/08_Development_Details/03_Cache/03_CDN_Integration.md for the core
mechanics (tags, messenger transport pimcore_cdn_purge, pimcore:cdn:purge command).
Providers
CDN_PROVIDER |
Tag purge | URL purge | Image transform (CDN_IMAGE_OPTIMIZER) |
Extra dependency |
|---|---|---|---|---|
cloudflare |
yes | yes | cloudflare (/cdn-cgi/image/) |
– |
google |
yes | yes | – | google/auth |
aws |
yes (#tag items, needs CacheTagConfig) or via path mapping |
yes | – | aws/aws-sdk-php |
azure |
via path mapping | yes | – | – |
chain |
every provider in chain.providers |
yes | – | – |
Stacked CDNs (chain)
For setups with two CDN layers, e.g. Cloudflare in front of Azure Front Door, set
CDN_PROVIDER=chain and list the providers in cors_cdn.chain.providers. Every purge goes
to all of them, in the listed order. Put the layer closest to the origin first, otherwise the
outer CDN can refetch the stale object from the inner one before that is purged.
If one provider fails, the others still run; the first failure is re-thrown afterwards so
Messenger retries the message (re-purging the successful ones, which is harmless). Any
registered purge client works in a chain, including Pimcore's fastly. The Cache-Tag
header is rewritten to commas if any chained provider needs it.
Cache-Tag header
Pimcore writes Cache-Tag space-separated. Cloudflare, CloudFront and Cloud CDN read
comma-separated tags, so CacheTagHeaderRewriteListener rewrites the header for those
providers (priority -10, after Pimcore's listener). Surrogate-Key is left untouched.
Providers without cache tags
Azure Front Door has no cache tags, and CloudFront only has them when the distribution
carries a CacheTagConfig (set HeaderName to Cache-Tag; aws.tag_invalidation: false
switches to path mapping). For path-only purges TagToPathMapper translates Pimcore's tags:
| Tag | Path pattern |
|---|---|
asset-{id} |
{thumbnail prefix}{asset folder}/{id}/* (asset must still exist) |
asset-{id}-thumb-{config} |
…/{id}/image-thumb__{id}__{config}/* and video-thumb… |
thumb-{config} |
unmappable |
asset-path-{hash} |
unmappable (originals are URL-purged anyway) |
Unmappable tags are skipped with a warning, or with unmappable_tag_strategy: purge_all
collapsed into one /* invalidation. Deleted assets cannot be resolved, so their
thumbnails expire by TTL; the original is still purged by URL.
Installation
composer require cors/pimcore-cdn:^2026.1
Enable CORS\Bundle\CdnBundle\CORSCdnBundle, set CDN_PROVIDER and CDN_BASE_URL, and
run a worker for the pimcore_cdn_purge transport.
Configuration
cors_cdn: cloudflare: zone_id: '%env(CLOUDFLARE_ZONE_ID)%' api_token: '%env(CLOUDFLARE_API_TOKEN)%' google: project: '%env(GOOGLE_CLOUD_PROJECT)%' url_map: '%env(GOOGLE_CDN_URL_MAP)%' max_tags_per_request: 100 # Google documents no hard limit aws: distribution_id: '%env(AWS_CLOUDFRONT_DISTRIBUTION_ID)%' region: us-east-1 tag_invalidation: true # false = path mapping azure: resource_id: '%env(AZURE_FRONTDOOR_RESOURCE_ID)%' tenant_id: '%env(AZURE_TENANT_ID)%' client_id: '%env(AZURE_CLIENT_ID)%' client_secret: '%env(AZURE_CLIENT_SECRET)%' api_version: '2024-02-01' chain: providers: [azure, cloudflare] # only used with CDN_PROVIDER=chain; origin-side first path_only_purge: unmappable_tag_strategy: skip # skip | purge_all
Google uses Application Default Credentials (GOOGLE_APPLICATION_CREDENTIALS or Workload
Identity) with the compute scope; the service account needs compute.urlMaps.invalidateCache.
AWS uses the default SDK credential chain and needs cloudfront:CreateInvalidation.
Azure uses an Entra ID app registration (client credentials) with the CDN Profile Contributor
role on the Front Door profile; resource_id is the ARM id of the AFD endpoint.
Google and AWS clients are only registered when google/auth respectively aws/aws-sdk-php
is installed.
Not yet verified against live APIs
- Cloudflare
trim.*crop options in the transform adapter. - Google's maximum number of
cacheTagsper request and invalidation rate limits. - Whether Azure Front Door honours partial-folder wildcards such as
/folder/42/*(documented is/folder/*); if not,asset-{id}purges on Azure needpurge_all.
Development
docker compose up -d docker compose exec php composer install docker compose exec php bin/console pimcore:install --profile cors-cdn-studio docker compose exec php composer test
License
MIT, see LICENSE.md.