Serving AVIF With Adaptive Quality Based on Network Speed
AI generated
60fps
ms
Performance · Image Formats · Network Adaptation
AVIF With Adaptive Quality
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.

14 min read AVIF · Client Hints Save-Data · Image CDN

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.

11. FAQ: AVIF Adaptive Quality: The Key Points

1What is the difference between Save-Data and Downlink?
Save-Data carries an explicit user decision for lower data usage, while Downlink is a technical estimate of current bandwidth. Save-Data should take priority in the decision.
2Do I need to send Accept-CH to receive Save-Data?
No, Save-Data is a low-entropy Client Hint and is sent automatically once the visitor enables data saver mode. Downlink and RTT, however, require an Accept-CH opt-in.
3How many quality tiers make sense?
Three to four fixed tiers are a good balance between adaptation accuracy and cache hit rate. More tiers increase the number of edge cache variants without noticeably improving perceived quality.
4Why doesn't Safari support the Network Information API?
Apple has not implemented the API so far, likely for privacy reasons, since connection data could serve as a fingerprinting signal. Server-side Client Hints work as a fallback regardless.
5How do I prevent adaptive images from bloating the edge cache?
By quantizing the raw Client Hints values down to the few defined quality tiers before the caching decision, instead of using the exact measured value as the cache key.
6Should every image category use the same quality tiers?
No, product photos with heavy detail texture tolerate less compression than flat graphics. A higher minimum quality for purchase-relevant images makes sense.
7Does adaptive quality replace responsive sizing via srcset?
No, the two mechanisms complement each other. srcset controls image size based on viewport, adaptive quality controls compression strength based on network condition.
8How do I check whether a quality tier is still acceptable?
With objective metrics like SSIM, DSSIM or Butteraugli in the build pipeline, instead of subjectively judging individual sample images.
9Does this approach work without an external image CDN?
Yes, the logic can also be implemented in your own transcoding pipeline, though with more effort for scaling and edge caching that a specialized CDN already provides.
10Does RTT influence the quality decision more than downlink?
Under high latency despite otherwise usable bandwidth, for example on satellite-based connections, RTT is often the more telling signal for actual user experience and should be evaluated alongside downlink.