CDN & Media Pipeline
Every image uploaded to VeroXM is automatically processed into the variants a real front end actually needs, and every content change can automatically purge your CDN's cache - both without any extra step at upload or publish time.
What happens on every image upload
MediaService runs each uploaded image through the same pipeline (built on sharp) before it is ever served:
- A same-format thumbnail, resized to 600px tall, for the media grid and picker.
- A WebP-encoded thumbnail, independent of the same-format one - a smaller download for a thumbnail-heavy grid, served via a
<picture>element that falls back to the original format automatically. - A full-resolution WebP re-encode of the original, for pages that render the image itself rather than a thumbnail card.
- A dominant-color tag, computed from a 1x1 average resize, used as an input to auto-tagging.
A format sharp cannot re-encode (BMP, and some GIFs) falls back to storing the original bytes as its own thumbnail rather than failing the upload - every image still gets a working thumbUrl, even if not every variant exists for it.
{
"fullUrl": "https://.../hero.jpg",
"fullWebpUrl": "https://.../hero.webp",
"thumbUrl": "https://.../hero-thumb.jpg",
"thumbWebpUrl": "https://.../hero-thumb.webp",
"width": 2400,
"height": 1350
}Storage providers
Where those files actually live is a swap-in StorageProvider, chosen per environment by a disk setting - the transform pipeline above runs identically regardless of which one is active, since it lives behind the interface rather than inside any one provider:
| Provider | Backing store |
|---|---|
local | Disk on the API server - the default for development. |
s3 | Amazon S3 (or an S3-compatible bucket). |
r2 | Cloudflare R2 - pairs naturally with Cloudflare CDN purging below. |
See Storage & Email Integrations for env var configuration, the object-key layout, and advanced deployment patterns for S3 and R2.
CDN cache purging
A project can connect a Cloudflare zone from its Integrations settings (zone id + API token). Once connected, every content.published, content.updated, and content.deleted event automatically triggers a purge - no manual cache-clearing step after a content change.
Purges run on the same background job queue as everything else, with a short coalescing delay: several rapid saves to the same project collapse into a single purge call rather than one per save, and a failed purge retries with backoff (3 attempts, 2s/4s) instead of silently leaving stale content behind the CDN.
One integration proves the pattern