A transforming proxy turns one stored image into as many renditions as your URLs ask for. That is the feature. It is also, if you never bound it, the bill — and the arithmetic is worth doing on paper before it gets done for you in production.

The multiplication

Every parameter a URL may carry is a dimension of the cache key. Suppose you allow, quite reasonably:

  • any width from 100 to 2000 pixels
  • any quality from 1 to 100
  • three output formats
  • four fit modes

That is 1901 × 100 × 3 × 4 — roughly 2.3 million distinct renditions. Of one image.

Nobody requests all of them, of course. But you do not need all of them for this to hurt. You need enough distinct values that your hit rate collapses, and that happens far earlier than the total suggests, because the requests are spread thinly across a huge space rather than concentrated on a few hot entries.

Where the odd values come from

Teams are often surprised that they have hundreds of widths in play. The usual sources:

  • A JavaScript component computing a width from the container. Every viewport width produces a different number. Phones alone give you dozens.
  • Device pixel ratio arithmetic. width × 2 on one device and × 3 on another, from a container that was already a fractional size.
  • Someone testing in production. A handful of one-off values that stay in the cache and in the logs forever.
  • Scrapers and scanners walking the parameter space deliberately.

What a miss actually costs

A cache hit is a lookup and some bytes. A miss is:

  1. a request to your origin for the source image, which you are billed for;
  2. a decode of that source;
  3. a resize;
  4. an encode — in AVIF, the expensive step;
  5. a write into the cache, evicting something else.

That last point is the one that compounds. A cache is finite. Every rendition nobody will ask for again pushes out one that somebody would have. So an unbounded parameter space does not merely add cost, it actively degrades the hit rate for the images you actually care about.

Bounding it

The fix is not clever, it is just deliberate: decide which values are allowed, and refuse the rest.

Pick widths from your layout

Your design has breakpoints. It does not have 1901 of them. Take the widths your layout actually renders at, double the ones that need to look right on high-density displays, and you will usually finish with somewhere between four and eight numbers.

allowed widths: 320, 640, 960, 1280, 1920

Fix quality, do not expose it

Quality is a decision your team makes once. There is very little reason for it to be a URL parameter that any caller can set — it multiplies the key space by up to a hundred in exchange for a knob nobody turns after launch.

Name the common combinations

A named preset collapses several dimensions into one token. ?preset=hero is one cache entry per image, whatever the preset happens to expand to, and changing the preset later is a single edit rather than a hunt through every template.

Refuse rather than adjust

When a request asks for a size you do not publish, there are two things a proxy can do: refuse it, or quietly serve the nearest allowed size instead.

Snapping is tempting — nothing visibly breaks. It is also a lie: the URL says w=1100 and the bytes are 1200 pixels wide, and two URLs have silently become one cache entry. Anyone debugging a layout later gets to discover that on their own.

Refusing is louder and, we think, correct. A 400 appears in your logs, names the problem, and gets fixed in the template where it originated. The URL keeps meaning what it says.

A worked example

Concretely, with five widths, one quality, negotiated format, one fit mode:

5 widths × 1 quality × 3 formats × 1 fit = 15 renditions per image

Fifteen, against 2.3 million. The images are identical; the difference is entirely in what the URLs were permitted to say. Fifteen fits in a cache, warms up in minutes, and costs your origin fifteen fetches rather than an open-ended number.

That is the whole argument for bounding a parameter space, and it is why the console has a place to write those numbers down.