Form Rig

JXL, AVIF
Serve the Optimal Image Format with Nginx

Eric Fortis

Why let Nginx determine the best image format to serve?

  • There's no need to change client-side code.
  • Unlike <picture> tags, video posters have no standard way of specifying many URLs for fetching the optimal format supported by the client.

Here's how

Upload the images with the modern extension appended.

  • foo.png
  • foo.png.jxl
  • foo.png.avif
  • foo.png.webp

By the way, here's a script to convert them.

For dispatching the most efficient format, Nginx will check the Accept request header. For example, Chrome 85 and up say they support AVIF by sending its media (MIME) type:

Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8

nginx.conf 1

http {
  # ...

  map $http_accept $img_ext {
    ~image/jxl    '.jxl';
    ~image/avif   '.avif';
    ~image/webp   '.webp';
    default       '';
  }

  server {
    # ...
    location /images {
      add_header  Vary Accept;
      try_files   $uri$img_ext $uri =404;
    }
  }
}

On requests under /images, Nginx defines the custom variable $img_ext with the extension associated to the first matched media type regex of the map, using the Accept header ($http_accept) as input parameter.

Then, try_files checks if there's a file with that extension appended, like foo.png.avif, and serves it. The middle $uri is a fail-safe. For instance, maybe the AVIF isn't uploaded yet.

Lastly, Vary: Accept is set as a response header. This is only needed if the server has caching reverse proxies in front (e.g., intercepting CDNs). Otherwise, those proxies would always serve the same format sent to the first client that requested the particular image.2

conf/mime.types

Make sure the JXL and AVIF media types are registered.

types {
  # ...
  image/jxl   jxl;
  image/avif  avif;
}

Test

lenna-colors-test-image
#!/bin/sh

img=lenna-colors.png

for mime in image/avif image/webp image/png; do
  response_mime=$(curl -so /dev/null --head \
    --header "Accept: $mime" \
    --write-out "%{content_type}" $img)

  if [ "$response_mime" = $mime ]; then
    echo "OK $mime"
  else
    echo "FAIL $mime Got: $response_mime" >&2
    exit 1
  fi
done

References

1. E. Lazutkin (2014) Serve files with Nginx conditionally 2. R. Mulhuijzen (2014) Best Practices for Using the Vary Header

Sponsored by: