WebP and AVIF, straight from site.json
The built-in scaler now writes WebP and AVIF, encoder quality is yours to set per format in site.json, and changing either one takes effect on existing URLs without a manual cache clear.
Two ways to ask for a modern format
Docly scales images on two separate paths, and both can now produce WebP and AVIF.
From script, docly.scaleImage() takes the format as its last argument:
docly.scaleImage(bytes, 1200, 800, 0, 78, "webp");
// w h mode quality format On a published URL the extension of the display filename decides. linkImage() does not scale anything itself - it builds the URL, and the scaling and encoding happen when that URL is served:
/photo.jpg/1024x1024x0/product.webp -> WebP
/photo.jpg/1024x1024x0/product.avif -> AVIF Classic formats are encoded through GDI+, which is the fastest path available. WebP and AVIF go through Magick.NET, because GDI+ cannot write them at all.
One switch for the whole site
Passing the format on every call gets tedious. images.defaultFormat in #/site.json sets it once, for everything:
{
"images": {
"defaultFormat": "webp"
}
} An explicit format - in the call or in the URL - always wins over the default.
Quality is yours to set
Encoder quality is configuration now, and it takes three shapes:
"images": { "quality": 78 } // one value for every format
"images": { "quality": { "jpeg": 90, "webp": 78, "avif": 50 } } // per format
"images": { "quality": { "webp": 78, "default": 70 } } // per format + fallback Lookup runs per-format key -> scalar or default -> built-in default.
Format | Built-in default |
|---|---|
JPEG | 85 |
WebP | 78 |
AVIF | 50 |
PNG | Lossless - quality is ignored |
An invalid value falls through to the next level and anything above 100 is clamped, so a typo in site.json cannot break image serving.
Quality can never be set from the URL. Same ownership rule as approved sizes: an arbitrary quality in the URL would mean one cache entry per size x quality - an unbounded cache, sized by whoever is doing the requesting rather than by you.
WebP and AVIF need a locked size policy
Check this first. If your site still has the default
opensize policy, a requested WebP or AVIF on the public URL path is served as JPEG or PNG instead - including when the request came fromdefaultFormat. Setimages.sizePolicytoautoorstrictto get the modern formats.
The reason is cost. WebP and AVIF encode through Magick.NET and are markedly more CPU-expensive than GDI+, and open keeps no whitelist of sizes - so the dimensions in the URL are whatever the caller asks for. Locking the policy puts a known set of sizes around the expensive encoder.
docly.scaleImage() is not affected. Its arguments come from your own server-side code, not from a request URL.
Policy | Behaviour |
|---|---|
| Any size up to |
| Accepts approved and discovered sizes; an unknown size snaps to the nearest one. The list grows from your own traffic. |
| Same as auto, but an unknown size is rejected outright. |
Changes take effect on their own
Scaled images are cached under a generation segment whose fingerprint is derived from images.quality, images.defaultFormat and images.sizePolicy. Change any of them and existing URLs point at a new, empty generation, so the image is re-encoded the next time someone asks for it.
Cosmetic edits deliberately do not trigger that. Casing, jpg versus jpeg, scalar versus object, key order, and numbers written as strings all normalise to the same fingerprint.
Roll this out outside peak hours. Regeneration is lazy and follows your traffic, so a quality or format change produces a CPU spike shaped like your traffic curve - and AVIF through Magick.NET is the most expensive thing in that spike.
What this means for existing sites
JPEG on the URL-scaling path now encodes at the same quality as everywhere else, 85. If you want a higher value on that path, set it explicitly:
"images": { "quality": { "jpeg": 90 } } Check it worked
curl -sI "https://yoursite.com/photo.jpg/1024x1024x0/product.webp" | grep -i content-type
-> content-type: image/webp If it comes back image/jpeg, your size policy is still open.