Serving images that every browser can read

Cloudflare has added support for the HTTP Vary header on image responses. The feature lets an origin serve different image variants of the same URL, with Cloudflare's edge caching the correct variant for each requesting browser based on that browser's declared capabilities.

Image encoding has moved well beyond the era when JPEG was the only format worth considering. Codecs like WebP and AVIF offer smaller files and better quality, but not every browser understands every format. That becomes a problem whenever an intermediate cache sits between the browser and the origin. A cached WebP image gets served to a browser that doesn't support WebP, and the page breaks. The Vary header gives origins a way to keep serving multiple formats from a single URL while letting caches pick the right one per request.

How content negotiation works

Browsers include an Accept header with each request that lists the content types they can handle, in order of preference. When an origin responds, it can include a Vary: Accept header to signal that the response body depends on the Accept header sent with the request. A browser that sends different Accept values can therefore receive a different variant, and an intermediate cache knows to store each variant separately.

GET /page.html HTTP/1.1
Host: example.com
Connection: keep-alive
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36
Accept-Encoding: gzip, deflate, br

The sequence looks like this in practice:

HTTP/1.1 200 OK
Content-Length: 123456
Vary: Accept

One way to negotiate content is to have the browser ask the server what variants exist, then request the one it wants. That works, but it adds extra round trips for every resource. Cloudflare's implementation skips most of that by parsing the request's Accept header and forwarding the relevant values to the origin. Because the expected variant types are configured ahead of time through the API, Cloudflare knows what to ask for and the origin knows exactly what to return, avoiding the back-and-forth of full content negotiation.

Caching behavior and normalization

When Cloudflare receives an origin response that includes the Vary: Accept header, it caches the specific variant returned. A later request carrying a different Accept header that maps to a different variant creates a separate cache entry for that variant.

A few conditions apply:

  • Vary for Images is available for the file extensions avif, bmp, gif, jpg, jpeg, jp2, jpg2, png, tif, tiff, and webp.
  • The origin must send the Vary: Accept response header for those extensions to be served as variants.
  • If the origin sends Vary: Accept but does not deliver the expected variant, the response is not cached, and the cache status in the response headers will read BYPASS.
  • The set of variants the origin serves for each extension must be configured with Cloudflare so the edge can pick a variant without contacting the origin.

Configuration via API

Vary for Images is currently enabled through the Cloudflare API and is available for Pro, Business, and Enterprise plans. The API exposes endpoints to create, modify, delete, and fetch a variants rule for a zone.

To serve JPEG as well as WebP and AVIF variants for jpg and jpeg extensions, create a rule:

curl -X PATCH
"https://api.cloudflare.com/client/v4/zones/023e105f4ecef8ad9ca31a8372d0c353/cache/variants" \ 
	-H "X-Auth-Email: [email protected]" \ 
	-H "X-Auth-Key: 3xamp1ek3y1234" \ 
	-H "Content-Type: application/json" \ 
	--data 
'{"value":{"jpeg":["image/webp","image/avif"],"jpg":["image/webp","image/avif"]}}' 

To narrow the rule so only WebP variants are served:

curl -X PATCH 
"https://api.cloudflare.com/client/v4/zones/023e105f4ecef8ad9ca31a8372d0c353/cache/variants" \ 
	-H "X-Auth-Email: [email protected]" \ 
	-H "X-Auth-Key: 3xamp1ek3y1234" \ 
	-H "Content-Type: application/json" \ 
	--data 
'{"value":{"jpeg":["image/webp"],"jpg":["image/webp"]}}' 

To remove the rule entirely:

curl -X DELETE 
"https://api.cloudflare.com/client/v4/zones/023e105f4ecef8ad9ca31a8372d0c353/cache/variants" \ 
	-H "X-Auth-Email: [email protected]" \ 
	-H "X-Auth-Key: 3xamp1ek3y1234" 

To inspect the current rule for a zone:

curl -X GET 
"https://api.cloudflare.com/client/v4/zones/023e105f4ecef8ad9ca31a8372d0c3533/cache/variants" \
	-H "X-Auth-Email: [email protected]" \ 
	-H "X-Auth-Key: 3xamp1ek3y1234"

Purging a URL that has varied images removes all variants for that URL, not just one. A single purge of a file, tag, or hostname clears every potential out-of-date variant, so a changed image can be refreshed without tracking down each format separately.

Vary for Images joins Cloudflare's other image delivery options. Polish automatically strips metadata and compresses images to reduce download size. Image Resizing runs as a proxy on the edge cache and adjusts image dimensions and quality. Cloudflare for Images is a full-service option for hosting, resizing, optimizing, and delivering images.