Markus Michalski FAQ

CDN Product Images: Technical

Architecture, decision logic and caching of the Sylius plugin CDN Product Images.

Deutsche Version

Back to overview

CDN Product Images: Technical

This documentation always describes the latest published version.

Live Demo: Try this and other plugins live β€” no installation, no risk: demo.markus-michalski.net

Decision logic

graph TD
    A["Image path"] --> B{"Starts with http(s)?"}
    B -->|No| C["local upload, unchanged"]
    B -->|Yes| D{"Host matches CDN domain?"}
    D -->|No| E["foreign URL, passthrough"]
    D -->|Yes| F["extract s3 bucket/key"]
    F --> G{"Signed URL cached?"}
    G -->|Yes| H["return cached URL"]
    G -->|No| I["call signing service"]
    I --> J{"HTTP 200?"}
    J -->|Yes| K["cache 24h, signed URL"]
    J -->|No| L["cache 30s, original URL"]

Detecting "CDN image vs. local upload" is purely based on the path prefix (http(s)://) - Sylius' own UploadedImagePathGenerator only ever produces relative, hash-based paths that can never start that way. There is no new database column or migration for this.

Plugin structure

src/
β”œβ”€β”€ Cdn/                      # Signing core: config, URL signer, preset map
β”œβ”€β”€ Command/                  # mmd:cdn-product-images:setup
β”œβ”€β”€ Controller/Admin/         # Settings page + token removal
β”œβ”€β”€ DependencyInjection/       # Bundle extension, compiler pass, (empty) Configuration
β”œβ”€β”€ Entity/                   # SigningCredentials (singleton settings row)
β”œβ”€β”€ Form/Extension/           # cdnUrl field on Product-/TaxonImageType
β”œβ”€β”€ Form/Type/                # SigningCredentialsType (admin form)
β”œβ”€β”€ Imagine/                  # decorated imagine_filter Twig extension
β”œβ”€β”€ Menu/                      # Admin sidebar entry
β”œβ”€β”€ Migrations/                # Doctrine migration for the settings table
β”œβ”€β”€ Repository/                # SigningCredentialsRepository
β”œβ”€β”€ Security/                  # SigningTokenCipher (libsodium)
β”œβ”€β”€ Serializer/                # decorated Shop API image normalizer
└── Uploader/                  # CDN-aware image uploader + path detection

Decorated services

Decorated service Decorator class Purpose
liip_imagine.templating.filter_extension CdnAwareImagineFilterExtension makes imagine_filter CDN-aware on the Twig storefront/Admin
sylius.uploader.image CdnAwareImageUploader prevents a CDN path from being deleted as if it were a local file
sylius_api.normalizer.image CdnAwareApiImageNormalizer the same logic for the Shop API channel - only registered when SyliusApiBundle is present (its own compiler pass, load-order independent)

Entity & migration

Entity Table Purpose
SigningCredentials mmd_cdn_signing_credentials singleton row (id always 1) holding CDN domain, signing API URL, encrypted token ciphertext, scheme override

Not a Sylius Resource (no grid, no list, no create/delete) - the admin only ever edits this one row through the settings page.

Caching

What TTL Why
Successfully signed URL 24 hours signatures are deterministic - avoids a network round-trip per image render
Failed signing attempt 30 seconds bounds a retry storm against the signing API's rate limit during an outage
Resolved credentials (entity β†’ effective config) 60 seconds avoids a DB query per image render; an admin change takes effect within about a minute

Resize mapping

Every Sylius Imagine filter size is mapped 1:1 to an imgproxy resize option (including format: webp and quality: 80, so a CDN image matches the quality of a locally processed one). An unknown filter name always falls back to passthrough - a wrongly guessed resize would be worse than none at all.