Image compression that reacts to the visitor's actual network conditions
A single, fixed AVIF quality level applied to your whole audience is always a compromise between two extremes that rarely get served well at the same time. Visitors on a fast fiber connection receive images that are more compressed than necessary, while visitors on mobile networks with data saver enabled still pay for quality they explicitly asked to avoid. Client Hints, the Save-Data header, and a connection to an image CDN let you resolve the quality level per request instead of baking one number into the build once and for all.
Table of Contents
- 1. Why a fixed AVIF quality level is a poor fit for every visitor
- 2. Client Hints and the Save-Data header as a network quality signal
- 3. The trade-off between file size and perceived image quality
- 4. The Save-Data header in detail: respecting an explicit user preference
- 5. The Network Information API in the browser: effectiveType, downlink, saveData
- 6. Practical implementation with an image CDN
- 7. Server-side decision vs. client-side srcset variants
- 8. Caching pitfalls: the Vary header and CDN edge caching for adaptive images
- 9. Validating quality with measurement instead of gut feeling
- 10. Summary
- 11. FAQ
1. Why a fixed AVIF quality level is a poor fit for every visitor
Anyone who encodes AVIF once at build time with a single fixed quality, say quality 50 for every product image, makes a decision that is not actually optimal for any individual visitor, only an acceptable compromise on average. A visitor on an office fiber connection could easily load a higher quality level without any noticeable change in load time, yet receives the exact same compressed file as someone on a train with flaky LTE reception. Conversely, that same mobile visitor still pays more bytes than they would want under an active data saver setting, because the quality level never reacts to their actual situation.
The real problem is treating image quality as a global constant fixed once during deployment, rather than as a function of the context a given request happens in. For a shop with an internationally mixed, partly mobile-heavy audience, that translates into lost revenue at two ends at once: unnecessarily long load times for the weakest connection segment, and unused quality headroom for the strongest one. Adaptive quality resolves exactly this tension by determining the compression level at request time from real signals, instead of guessing it up front.
2. Client Hints and the Save-Data header as a network quality signal
Client Hints are HTTP headers through which the browser can hand the server structured information about the current connection, without any JavaScript having to measure or evaluate anything first. The Save-Data header belongs to the so-called low-entropy hints and is sent automatically once the visitor has enabled a data saver mode at the OS or browser level, with no need for the site to opt in through Accept-CH beforehand. On top of that, the high-entropy hints Downlink and RTT provide a rough bandwidth and latency estimate that the browser derives from past connections and that the server keeps receiving after a one-time opt-in via the Accept-CH response header.
The decisive advantage these headers have over a client-side measurement is that they can already be present on the very first request, before any JavaScript has loaded or executed at all. For a server-side or CDN-side decision about AVIF quality this matters, because the image request is often the first request after the initial HTML document and needs to hit the right quality level without waiting for a later JavaScript correction. A small middleware layer can read these headers directly and derive a quality level from them, as the example below shows.
// Node/Express middleware: derive a quality level from Client Hints
function resolveAvifQuality(req) {
const saveData = req.headers['save-data'] === 'on';
const downlink = parseFloat(req.headers['downlink'] || '10'); // Mbps
const rtt = parseInt(req.headers['rtt'] || '50', 10); // ms
if (saveData) return 40; // explicit user preference wins first
if (downlink < 1.5 || rtt > 400) return 45; // slow mobile network
if (downlink < 4) return 55; // mid-range connection
return 72; // fast connection, wifi/fiber
}
app.get('/img/:file.avif', (req, res) => {
const quality = resolveAvifQuality(req);
res.setHeader('Vary', 'Save-Data, Downlink, RTT');
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
return pipeAvifTranscode(req.params.file, quality, res);
});
3. The trade-off between file size and perceived image quality
AVIF compresses noticeably more efficiently than older formats thanks to modern intra-prediction and variable block sizes, but the relationship between the quality parameter and perceived quality is not linear. In the upper range, roughly between quality 80 and 95, file size barely drops in any way the human eye picks up, while the middle range between quality 40 and 65 already saves meaningful bytes without an average viewer on a typical mobile display reliably noticing the difference. That middle range is exactly the target corridor for adaptive systems that distinguish between two or three quality tiers instead of offering a continuous value.
It matters not to treat this trade-off the same way for every kind of image content, since product photos with heavy detail texture are more sensitive to compression artifacts than flat graphics or icons. A system that picks the quality level purely from network conditions should therefore define slightly different lower bounds per image category, for instance a higher minimum quality for hero and product images than for decorative background graphics, so the savings do not show up exactly where sharpness drives purchase decisions.
4. The Save-Data header in detail: respecting an explicit user preference
The Save-Data header differs conceptually from the bandwidth-based signals because it carries an explicit decision from the visitor rather than a purely technical measurement. When someone has enabled their device's data saver mode, they generally want to consciously reduce data consumption even if their actual connection is fast enough for high quality at that moment, for example on wifi under a throttled plan. That preference should therefore rank above the measured bandwidth values in the priority order, since otherwise it contradicts the visitor's explicit wish even though the measurement objectively reports more favorable numbers.
In practice this means evaluating Save-Data consistently for every data-intensive decision on a page, not just image quality, including things like loading video previews or high-resolution retina variants. If Save-Data is ignored and only the measured downlink value is used, the header loses its actual purpose, which is giving visitors in regions with expensive or limited data plans reliable control over their consumption, regardless of how fast their connection is technically measured to be.
5. The Network Information API in the browser: effectiveType, downlink, saveData
Besides the HTTP headers, the browser additionally exposes the navigator.connection interface, which surfaces the same signals client-side as a JavaScript object, plus effectiveType, a rough categorization into slow-2g, 2g, 3g or 4g. This API is particularly useful when the quality decision has to be made not at the initial server request but later, while loading further images during scrolling, for instance in a lazy-loading gallery that builds new image URLs only as items reach the viewport.
The limitation of this API is its patchy browser support, since Safari still does not implement navigator.connection, while Chromium-based browsers report the values reliably. A robust system therefore should not rely exclusively on the client-side API, but should treat it as a complement to the server-evaluated Client Hints, so that Safari visitors still receive a sensible quality level from the headers already present in the request, even though the fine-grained client-side adjustment is missing there.
6. Practical implementation with an image CDN
In practice the AVIF transcoding is rarely handled by the application itself, but by a specialized image CDN that stores original assets once and generates variants on first request, caching them at the edge afterward. Most of these services accept quality and format parameters directly in the URL, so the application only needs to append the computed quality level as a parameter, without implementing any encoding logic itself or carrying the processing load for image transformation. Evaluating the Client Hints happens either in a small middleware layer ahead of the image request that assembles the URL accordingly, or directly inside an edge function of the CDN provider, which has access to the same request headers.
It matters to deliberately keep the number of resulting URL variants small, say three or four fixed quality tiers instead of a continuous value range, because every additional variant dilutes the hit rate in the CDN's edge cache. A configuration with tiers 40, 55 and 72, as shown in the earlier code example, covers the relevant network situations while ensuring that many visitors with similar connection quality hit the same already generated and cached variant, instead of triggering a fresh transcode on every request.
7. Server-side decision vs. client-side srcset variants
As an alternative to a server-side quality decision, adaptive quality can also be modeled through an extended srcset with several pre-generated quality tiers, letting the browser choose between the candidates using its own heuristics. This approach has the advantage of working entirely without server-side header evaluation, but the drawback that the browser's heuristic is primarily tuned for display size and device pixel ratio and only partially factors in bandwidth signals, so the actual network adaptation ends up weaker than with a targeted server-side decision.
In practice a combination of both approaches works best: the server-side quality level determines which compression strength is used within a given size variant, while srcset continues to handle size selection based on viewport. That way two independent dimensions, image size and compression quality, are each controlled by the mechanism that has the most reliable access to the signals it needs.
8. Caching pitfalls: the Vary header and CDN edge caching for adaptive images
Once the response depends on request headers, a correctly set Vary header must ensure that an edge cache or an intermediary proxy does not accidentally serve the variant generated for a fast connection to a visitor with Save-Data active. At the same time, that very Vary header is a common cause of a poor cache hit rate, because every additional combination of header values potentially creates its own cache entry, and with continuous values like an exact downlink measurement the number of possible combinations would be effectively unbounded.
The fix is to never use the raw Client Hints values directly as a cache key, but to quantize them down to the small set of defined quality tiers before the caching decision, exactly as the middleware in the earlier example already does. The cache key is then based not on the exact downlink value but on the derived quality tier, keeping the number of variants per image limited to the deliberately small set while the hit rate at the edge stays high, even though the response remains context-dependent.
9. Validating quality with measurement instead of gut feeling
Which quality level is still acceptable for which network situation should not be decided by subjectively eyeballing a handful of sample images, but through objective metrics like SSIM, DSSIM or Butteraugli, which quantify perceived quality loss against the original. These metrics can be wired into the build pipeline to automatically check, for every new quality tier, whether the loss stays within a previously defined tolerance, instead of relying on a one-time manual judgment that might no longer hold for other image subjects.
It is also worth looking at real user data from Real User Monitoring to check whether the chosen bandwidth thresholds actually match the connection types present in your own visitor base. A shop with a mostly European fixed-line audience needs different thresholds than one with a large share of mobile visitors in regions with weak network coverage, and only an analysis of your own data reliably shows where the three or four quality tiers should be set.
| Signal | Source | Value range | Consequence for the quality tier |
|---|---|---|---|
| Save-Data | HTTP header, low-entropy Client Hint | on / not set | Force the lowest quality tier, user preference wins |
| Downlink | HTTP header or navigator.connection.downlink | Mbps estimate, e.g. 0.4 to 10+ | Pick a quality tier based on the bandwidth bucket |
| RTT | HTTP header or navigator.connection.rtt | Milliseconds | Treat high latency as an additional mobile-network signal alongside bandwidth |
| effectiveType | navigator.connection.effectiveType, client-side only | slow-2g / 2g / 3g / 4g | Rough category fallback when downlink is missing |
| Viewport width | Sec-CH-Viewport-Width or measured client-side | Pixel value | Combine with quality: adjust size and compression together |
Mironsoft
Web performance, Core Web Vitals, and load time optimization
Load times that don't make users bounce before the page is even visible?
We review existing websites for slow Core Web Vitals, bloated JavaScript bundles, and unnecessary render blockers, then build a performance foundation that stays measurable instead of just looking good once.
Performance Audit
Systematically measuring and fixing Core Web Vitals, load waterfall, and render blockers.
Bundle Optimization
Specifically reducing JavaScript and CSS bundle size and improving code splitting.
Monitoring Setup
Establishing continuous performance monitoring instead of a one-time snapshot.
10. Summary
AVIF Adaptive Quality: The Key Points
Core idea
The AVIF quality tier is resolved per request dynamically, not baked into the build once and for all.
Most important signal
Save-Data outranks measured bandwidth, because it carries an explicit user preference.
Implementation
An image CDN generates variants from a few fixed quality tiers instead of a continuous value.
Caching
Quantize the raw Client Hints values down to a few tiers before the caching decision, or the hit rate drops.